├── .circleci └── config.yml ├── .gitignore ├── Docker.md ├── Dockerfile ├── Dockerfile.alpine ├── LICENSE ├── Makefile ├── README.md ├── ack.go ├── addchain.go ├── addentry.go ├── addresses.go ├── byheight.go ├── completion.go ├── diagnostics.go ├── fctCmd.go ├── get.go ├── go.mod ├── go.sum ├── help.go ├── help_test.go ├── identity.go ├── identityKeys.go ├── main.go ├── raw.go ├── receipt.go ├── replaystates.go ├── transaction.go ├── transaction_test.go ├── unlock.go ├── util.go ├── util_test.go └── walletbackup.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | 5 | build: 6 | working_directory: /go/src/github.com/FactomProject/factom-cli 7 | docker: 8 | - image: circleci/golang:1.15 9 | 10 | steps: 11 | - checkout 12 | 13 | - run: 14 | name: Build and install the executable 15 | command: go install -v 16 | 17 | test: 18 | working_directory: /go/src/github.com/FactomProject/factom-cli 19 | docker: 20 | - image: circleci/golang:1.15 21 | 22 | steps: 23 | - checkout 24 | 25 | - run: 26 | name: Build and install the executable 27 | command: go install -v 28 | 29 | - run: 30 | name: Run tests! 31 | no_output_timeout: 2400 32 | command: go test -v ./... 33 | 34 | # Docker builds 35 | docker_build: 36 | working_directory: /go/src/github.com/FactomProject/factom-cli 37 | 38 | docker: 39 | - image: factominc/elixir_python 40 | 41 | steps: 42 | - checkout 43 | 44 | - setup_remote_docker: 45 | version: 17.06.0-ce 46 | 47 | # Load the images if the cache hit 48 | - run: 49 | name: Load Docker image layer cache 50 | command: | 51 | set +o pipefail 52 | docker load -i /images/factom-cli-alpine.tar | true 53 | docker load -i /images/factom-cli.tar | true 54 | 55 | # Build the containers 56 | - run: 57 | name: Build the baseline images 58 | command: | 59 | docker build -t factom-cli-alpine -f Dockerfile.alpine . 60 | docker build -t factom-cli -f Dockerfile . 61 | 62 | # Push, depending on branch/tag 63 | - run: 64 | name: Push master / develop to DockerHub 65 | command: | 66 | 67 | # Login to DockerHub 68 | docker login -u $DOCKER_USER -p $DOCKER_PASSWORD 69 | 70 | # If there is a tag, use it 71 | if [ "$CIRCLE_TAG" != "" ]; then 72 | docker tag factom-cli factominc/factom-cli:${CIRCLE_TAG} 73 | docker push factominc/factom-cli:${CIRCLE_TAG} 74 | docker tag factom-cli-alpine factominc/factom-cli:${CIRCLE_TAG}-alpine 75 | docker push factominc/factom-cli:${CIRCLE_TAG}-alpine 76 | fi 77 | 78 | if [ "${CIRCLE_BRANCH}" == "master" ]; then 79 | 80 | # Tag master as latest 81 | docker tag factom-cli factominc/factom-cli:latest 82 | docker push factominc/factom-cli:latest 83 | docker tag factom-cli-alpine factominc/factom-cli:alpine 84 | docker push factominc/factom-cli:alpine 85 | fi 86 | 87 | if [ "${CIRCLE_BRANCH}" == "develop" ]; then 88 | 89 | # Tag develop as develop anyhow 90 | docker tag factom-cli factominc/factom-cli:develop 91 | docker push factominc/factom-cli:develop 92 | docker tag factom-cli-alpine factominc/factom-cli:alpine-develop 93 | docker push factominc/factom-cli:alpine-develop 94 | fi 95 | 96 | docker logout 97 | 98 | - run: 99 | name: Push images to AWS ECR 100 | command: | 101 | # Login to AWS ECR 102 | login="$(aws ecr get-login --no-include-email --region $AWS_REGION)" 103 | ${login} 104 | 105 | # If there is a tag, use it 106 | if [ "$CIRCLE_TAG" != "" ]; then 107 | docker tag factom-cli ${AWS_REPO_BASE_URI}/factom-cli:${CIRCLE_TAG} 108 | docker push ${AWS_REPO_BASE_URI}/factom-cli:${CIRCLE_TAG} 109 | docker tag factom-cli-alpine ${AWS_REPO_BASE_URI}/factom-cli:${CIRCLE_TAG}-alpine 110 | docker push ${AWS_REPO_BASE_URI}/factom-cli:${CIRCLE_TAG}-alpine 111 | fi 112 | 113 | if [ "${CIRCLE_BRANCH}" == "master" ]; then 114 | 115 | # Tag master as latest 116 | docker tag factom-cli ${AWS_REPO_BASE_URI}/factom-cli:latest 117 | docker push ${AWS_REPO_BASE_URI}/factom-cli:latest 118 | docker tag factom-cli-alpine ${AWS_REPO_BASE_URI}/factom-cli:alpine 119 | docker push ${AWS_REPO_BASE_URI}/factom-cli:alpine 120 | fi 121 | 122 | if [ "${CIRCLE_BRANCH}" == "develop" ]; then 123 | 124 | # Tag develop as develop anyhow 125 | docker tag factom-cli ${AWS_REPO_BASE_URI}/factom-cli:develop 126 | docker push ${AWS_REPO_BASE_URI}/factom-cli:develop 127 | docker tag factom-cli-alpine ${AWS_REPO_BASE_URI}/factom-cli:alpine-develop 128 | docker push ${AWS_REPO_BASE_URI}/factom-cli:alpine-develop 129 | fi 130 | 131 | docker logout 132 | 133 | - run: 134 | name: Save the image cache 135 | command: | 136 | mkdir -p /images 137 | docker save -o /images/factom-cli-alpine.tar factom-cli-alpine $(docker history -q factom-cli-alpine |grep -v missing | grep -v none) 138 | docker save -o /images/factom-cli.tar factom-cli $(docker history -q factom-cli |grep -v missing | grep -v none) 139 | 140 | 141 | # The flow is 142 | # build 143 | # | 144 | # ---------- test 145 | # | 146 | # ---------- docker_build 147 | # 148 | # 149 | 150 | workflows: 151 | version: 2 152 | commit-workflow: 153 | jobs: 154 | - build: 155 | filters: 156 | tags: 157 | only: /.*/ 158 | - test: 159 | filters: 160 | tags: 161 | only: /.*/ 162 | requires: 163 | - build 164 | - docker_build: 165 | filters: 166 | tags: 167 | only: /.*/ 168 | branches: 169 | only: 170 | - develop 171 | - master 172 | context: org-global 173 | requires: 174 | - test 175 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Executable from build 2 | factom-cli 3 | 4 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 5 | *.o 6 | *.a 7 | *.so 8 | 9 | # Temp files 10 | *~ 11 | *.kate-swp 12 | *.orig 13 | *.iml 14 | 15 | # Folders 16 | _obj 17 | _test 18 | bin 19 | build 20 | pkg 21 | .idea 22 | vendor/ 23 | 24 | # Architecture specific extensions/prefixes 25 | *.[568vq] 26 | [568vq].out 27 | 28 | *.cgo1.go 29 | *.cgo2.c 30 | _cgo_defun.c 31 | _cgo_gotypes.go 32 | _cgo_export.* 33 | 34 | _testmain.go 35 | 36 | *.exe 37 | *.test 38 | 39 | .DS_Store 40 | -------------------------------------------------------------------------------- /Docker.md: -------------------------------------------------------------------------------- 1 | # factom-cli Docker Helper 2 | 3 | The factom-cli Docker Helper is a simple tool to help build and run factom-cli as a container 4 | 5 | ## Prerequisites 6 | 7 | You must have at least Docker v17 installed on your system. 8 | 9 | Having this repo cloned helps too 😇 10 | 11 | ## Build 12 | From wherever you have cloned this repo, run 13 | 14 | `docker build -t factom-cli_container .` 15 | 16 | (yes, you can replace **factom-cli_container** with whatever you want to call the container. e.g. **factom-cli**, **foo**, etc.) 17 | 18 | #### Cross-Compile 19 | To cross-compile for a different target, you can pass in a `build-arg` as so 20 | 21 | `docker build -t factom-cli_container --build-arg GOOS=darwin .` 22 | 23 | ## Run 24 | `docker run --rm factom-cli_container ` 25 | 26 | e.g. 27 | 28 | `docker run --rm factom-cli_container get heights` 29 | 30 | **Note** - In the above, replace **factom-cli_container** with whatever you called it when you built it - e.g. **factom-cli**, **foo**, etc. 31 | 32 | 33 | ## Copy 34 | So yeah, you want to get your binary _out_ of the container. To do so, you basically mount your target into the container, and copy the binary over, like so 35 | 36 | 37 | `docker run --rm --entrypoint='' -v :/destination factom-cli_container /bin/cp /go/bin/factom-cli /destination` 38 | 39 | e.g. 40 | 41 | `docker run --rm --entrypoint='' -v /tmp:/destination factom-cli_container /bin/cp /go/bin/factom-cli /destination` 42 | 43 | which will copy the binary to `/tmp/factom-cli` 44 | 45 | **Note** : You should replace ** factom-cli_container** with whatever you called it in the **build** section above e.g. **factom-cli**, **foo**, etc. 46 | 47 | #### Cross-Compile 48 | If you cross-compiled to a different target, your binary will be in `/go/bin//factom-cli`. e.g. If you built with `--build-arg GOOS=darwin`, then you can copy out the binary with 49 | 50 | `docker run --rm --entrypoint='' -v :/destination factom-cli_container /bin/cp /go/bin/darwin_amd64/factom-cli /destination` 51 | 52 | e.g. 53 | 54 | `docker run --rm --entrypoint='' -v /tmp:/destination factom-cli_container /bin/cp /go/bin/darwin_amd64/factom-cli /destination` 55 | 56 | which will copy the darwin_amd64 version of the binary to `/tmp/factom-cli` 57 | 58 | **Note** : You should replace ** factom-cli_container** with whatever you called it in the **build** section above e.g. **factom-cli**, **foo**, etc. 59 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM golang:1.15 3 | 4 | # Where factom-cli sources will live 5 | WORKDIR $GOPATH/src/github.com/FactomProject/factom-cli 6 | 7 | # Populate the rest of the source 8 | COPY . . 9 | 10 | ARG GOOS=linux 11 | 12 | # Build and install factom-cli 13 | RUN make install 14 | 15 | ENTRYPOINT ["/go/bin/factom-cli"] 16 | -------------------------------------------------------------------------------- /Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | FROM golang:1.15-alpine as builder 2 | 3 | RUN apk add --no-cache git make gcc libc-dev 4 | 5 | # Where factom-cli sources will live 6 | WORKDIR $GOPATH/src/github.com/FactomProject/factom-cli 7 | 8 | # Populate the rest of the source 9 | COPY . . 10 | 11 | ARG GOOS=linux 12 | 13 | # Build and install factom-cli 14 | RUN make install 15 | 16 | # Now squash everything 17 | FROM alpine:3.12 18 | 19 | # Get git 20 | RUN apk add --no-cache ca-certificates curl git 21 | 22 | RUN mkdir -p /go/bin 23 | 24 | COPY --from=builder /go/bin/factom-cli /go/bin/factom-cli 25 | 26 | ENTRYPOINT ["/go/bin/factom-cli"] 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Factom Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | The following software may be included in this product: 31 | basen, Copyright (c) 2014, Casey Marshall 32 | bolt, Copyright (c) 2013, Ben Johnson 33 | go-bip32, Copyright (c) 2014, Tyler Smith 34 | go-bip39, Copyright (c) 2014, Tyler Smith 35 | go-simplejson, Copyright (c) 2015, Matt Reiferson 36 | web, Copyright (c) 2009, Michael Hoisie 37 | log, Copyright (c) 2014 Alexandre Cesaro 38 | 39 | Use of any of this software is governed by the terms and conditions set forth below: 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 42 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 44 | END OF TERMS AND CONDITIONS 45 | 46 | The following software may be included in this product: 47 | btcd, Copyright (c) 2013-2015, The btcsuite developers 48 | btclog, Copyright (c) 2013-2014, Conformal Systems LLC 49 | btcrpcclient, Copyright (c) 2014-2015 Conformal Systems LLC 50 | btcutil, Copyright (c) 2013 Conformal Systems LLC 51 | fastsha256, Copyright (c) 2013 Conformal Systems LLC 52 | go-spew, Copyright (c) 2012-2013 Dave Collins 53 | 54 | Use of any of this software is governed by the terms of the terms and conditions set forth below: 55 | 56 | Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 59 | 60 | END OF TERMS AND CONDITIONS 61 | 62 | The following software may be included in this product: 63 | btcutilecc, 64 | Copyright (c) 2010 The Go Authors. All rights reserved. 65 | Copyright (c) 2011 ThePiachu. All rights reserved. 66 | Copyright (c) 2013 Michael Hendricks. All rights reserved. 67 | ed25519, Copyright (c) 2012 The Go Authors. All rights reserved. 68 | fastsha 256, Copyright 2013 The Go Authors. All rights reserved. 69 | go-socks, Copyright (c) 2012, Samuel Stauffer samuel@descolada.com All rights reserved. 70 | golangcrypto, Copyright (c) 2009 The Go Authors. All rights reserved. 71 | netki-go-partner-client, Copyright (c) 2015, Netki, Inc. 72 | seelog, Copyright (c) 2012, Cloud Instruments Co., Ltd. info@cin.io All rights reserved. 73 | snappy-go, Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. 74 | 75 | Use of any of this software is governed by the terms of the terms and conditions set forth below: 76 | 77 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 78 | 79 | * Redistributions of source code must retain the above copyright 80 | notice, this list of conditions and the following disclaimer. 81 | 82 | * Redistributions in binary form must reproduce the above 83 | copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 84 | 85 | * Neither the name of the author nor the names of its 86 | contributors may be used to endorse or promote products derived from this software without specific prior written permission. 87 | 88 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 89 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 90 | 91 | END OF TERMS AND CONDITIONS 92 | 93 | The following software may be included in this product: 94 | goleveldb, Copyright 2012, Suryandaru Triandana syndtr@gmail.com All rights reserved. 95 | websocket, Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 96 | 97 | Use of any of this software is governed by the terms of the terms and conditions set forth below: 98 | 99 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 100 | 101 | * Redistributions of source code must retain the above copyright 102 | notice, this list of conditions and the following disclaimer. 103 | 104 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 105 | 106 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 107 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 108 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 109 | 110 | END OF TERMS AND CONDITIONS 111 | 112 | 113 | The following software may be included in this product: 114 | staticfiles, Copyright (c) 2016 Bouke van der Bijl 115 | 116 | Use of any of this software is governed by the terms and conditions set forth below: 117 | 118 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 119 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 120 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 121 | END OF TERMS AND CONDITIONS 122 | 123 | “Font Awesome” version 4.7.0, Copyright Dave Gendy, is used by this product and is licensed under the following terms and conditions: 124 | SIL OPEN FONT LICENSE 125 | 126 | Version 1.1 - 26 February 2007 127 | 128 | PREAMBLE 129 | 130 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. 131 | 132 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. 133 | 134 | DEFINITIONS 135 | 136 | "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. 137 | 138 | "Reserved Font Name" refers to any names specified as such after the copyright statement(s). 139 | 140 | "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). 141 | 142 | "Modified Version" refers to any derivative made by adding to, deleting, or substituting — in part or in whole — any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. 143 | 144 | "Author" refers to any designer, engineer, programmer, technical 145 | writer or other person who contributed to the Font Software. 146 | 147 | PERMISSION & CONDITIONS 148 | 149 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 150 | 151 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 152 | 153 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 154 | 155 | 3) No Modified Version of the Font Software may use the Reserved 156 | Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 157 | 158 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 159 | 160 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. 161 | 162 | TERMINATION 163 | 164 | This license becomes null and void if any of the above conditions are not met. 165 | 166 | DISCLAIMER 167 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 168 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. 169 | 170 | END OF TERMS AND CONDITIONS 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REVISION = $(shell git describe --tags) 2 | $(info Make factom-cli $(REVISION)) 3 | 4 | LDFLAGS := "-s -w -X main.FactomcliVersion=$(REVISION)" 5 | 6 | default: factom-cli 7 | install: factom-cli-install 8 | all: factom-cli-darwin-amd64 factom-cli-windows-amd64.exe factom-cli-windows-386.exe factom-cli-linux-amd64 factom-cli-linux-arm64 factom-cli-linux-arm7 9 | 10 | BUILD_FOLDER := build 11 | 12 | factom-cli: 13 | go build -trimpath -ldflags $(LDFLAGS) 14 | factom-cli-install: 15 | go install -trimpath -ldflags $(LDFLAGS) 16 | 17 | factom-cli-darwin-amd64: 18 | env GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags $(LDFLAGS) -o $(BUILD_FOLDER)/factom-cli-darwin-amd64-$(REVISION) 19 | factom-cli-windows-amd64.exe: 20 | env GOOS=windows GOARCH=amd64 go build -trimpath -ldflags $(LDFLAGS) -o $(BUILD_FOLDER)/factom-cli-windows-amd64-$(REVISION).exe 21 | factom-cli-windows-386.exe: 22 | env GOOS=windows GOARCH=386 go build -trimpath -ldflags $(LDFLAGS) -o $(BUILD_FOLDER)/factom-cli-windows-386-$(REVISION).exe 23 | factom-cli-linux-amd64: 24 | env GOOS=linux GOARCH=amd64 go build -trimpath -ldflags $(LDFLAGS) -o $(BUILD_FOLDER)/factom-cli-linux-amd64-$(REVISION) 25 | factom-cli-linux-arm64: 26 | env GOOS=linux GOARCH=arm64 go build -trimpath -ldflags $(LDFLAGS) -o $(BUILD_FOLDER)/factom-cli-linux-arm64-$(REVISION) 27 | factom-cli-linux-arm7: 28 | env GOOS=linux GOARCH=arm GOARM=7 go build -trimpath -ldflags $(LDFLAGS) -o $(BUILD_FOLDER)/factom-cli-linux-arm7-$(REVISION) 29 | 30 | .PHONY: clean 31 | 32 | clean: 33 | rm -f factom-cli 34 | rm -rf build 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Factom Command Line Interface 2 | 3 | [![CircleCI](https://circleci.com/gh/FactomProject/factom-cli/tree/develop.svg?style=shield)](https://circleci.com/gh/FactomProject/factom-cli/tree/develop) 4 | 5 | `factom-cli` provides a convenient CLI interface for making and viewing entries 6 | and transactions on the Factom blockchain by calling out to both 7 | [`factomd`](https://github.com/FactomProject/factomd) and 8 | [`factom-walletd`](https://github.com/FactomProject/factomd). 9 | 10 | ## Dependencies 11 | ### Build Dependencies 12 | - Go 1.13 or higher 13 | 14 | ### External Dependencies 15 | - Access to a `factomd` API endpoint 16 | - Access to a `factom-walletd` API endpoint 17 | 18 | ### Optional Dependencies 19 | - [CLI completion](https://github.com/AdamSLevy/complete-factom-cli) for Bash, 20 | Zsh or Fish 21 | 22 | ## Package distribution 23 | 24 | Binaries for your platform can be downloaded from the [GitHub release page](https://github.com/FactomProject/factom-cli/releases). 25 | 26 | ## Build and install 27 | 28 | ``` 29 | make install 30 | ``` 31 | 32 | To cross compile to all supported platforms: 33 | ``` 34 | make all 35 | ``` 36 | -------------------------------------------------------------------------------- /ack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/hex" 9 | "flag" 10 | "fmt" 11 | "os" 12 | "strings" 13 | 14 | "github.com/FactomProject/factom" 15 | "github.com/posener/complete" 16 | ) 17 | 18 | var status = func() *fctCmd { 19 | cmd := new(fctCmd) 20 | cmd.helpMsg = "factom-cli status [-TSUD] TxID|FullTx" 21 | cmd.description = "Returns information about a factoid transaction, or an" + 22 | " entry / entry credit transaction" 23 | cmd.completion = complete.Command{ 24 | Flags: complete.Flags{ 25 | "-T": complete.PredictNothing, 26 | "-S": complete.PredictNothing, 27 | "-U": complete.PredictNothing, 28 | "-D": complete.PredictNothing, 29 | }, 30 | } 31 | cmd.execFunc = func(args []string) { 32 | os.Args = args 33 | tdisp := flag.Bool("T", false, "display the transaction id only") 34 | sdisp := flag.Bool("S", false, "display the transaction status only") 35 | udisp := flag.Bool("U", false, "display the unix time of the transaction") 36 | ddisp := flag.Bool("D", false, "display the time of the transaction") 37 | flag.Parse() 38 | args = flag.Args() 39 | 40 | if len(args) < 1 { 41 | fmt.Println(cmd.helpMsg) 42 | return 43 | } 44 | tx := args[0] 45 | 46 | txID := "" 47 | fullTx := "" 48 | 49 | _, err := hex.DecodeString(strings.Replace(tx, "\"", "", -1)) 50 | if len(tx) == 64 && err == nil { 51 | txID = tx 52 | } else { 53 | if len(tx) < 64 || err != nil { 54 | t, err := factom.GetTmpTransaction(tx) 55 | if err != nil { 56 | errorln(err) 57 | return 58 | } 59 | txID = t.TxID 60 | } else { 61 | fullTx = strings.Replace(tx, "\"", "", -1) 62 | } 63 | } 64 | 65 | // Putting all 0s indicates an entry. If this fails, we will have to check if it's a commit 66 | eack, err := factom.EntryRevealACK(txID, fullTx, "0000000000000000000000000000000000000000000000000000000000000000") 67 | if err != nil { 68 | errorln(err) 69 | return 70 | } 71 | 72 | // Check if its an entry commit 73 | ecack, err := factom.EntryCommitACK(txID, fullTx) 74 | if err != nil { 75 | errorln(err) 76 | return 77 | } 78 | 79 | // If entryack returns unknown, and the commit is found to have the commitTxID 80 | if ecack != nil && (eack == nil || (eack != nil && eack.EntryData.Status == "Unknown")) { 81 | if ecack.CommitTxID != "" && ecack.CommitTxID == txID && ecack.CommitData.Status != "Unknown" { 82 | switch { 83 | case *tdisp: 84 | fmt.Println(ecack.CommitTxID) 85 | case *sdisp: 86 | fmt.Println(ecack.CommitData.Status) 87 | case *udisp: 88 | fmt.Println(ecack.CommitData.TransactionDate) 89 | case *ddisp: 90 | fmt.Println(ecack.CommitData.TransactionDateString) 91 | default: 92 | fmt.Println(ecack) 93 | } 94 | 95 | return 96 | } 97 | } 98 | 99 | // If it's not a commit, it could be an entry hash that is unknown. We filtered that out earlier, so now include it 100 | if eack != nil { 101 | // You searched for an entry by hash 102 | if eack.EntryHash != "" && eack.EntryHash == txID && (eack.EntryData.Status != "Unknown" || eack.CommitData.Status != "Unknown") { 103 | switch { 104 | case *tdisp: 105 | fmt.Println(eack.EntryHash) 106 | case *sdisp: 107 | fmt.Println(eack.EntryData.Status) 108 | case *udisp: 109 | fmt.Println(eack.EntryData.TransactionDate) 110 | case *ddisp: 111 | fmt.Println(eack.EntryData.TransactionDateString) 112 | default: 113 | fmt.Println(eack) 114 | } 115 | return 116 | } 117 | } 118 | 119 | // Check if its a factoid transaction 120 | fcack, err := factom.FactoidACK(txID, fullTx) 121 | if err != nil { 122 | errorln(err) 123 | return 124 | } 125 | 126 | if fcack != nil { 127 | if fcack.Status != "Unknown" { 128 | switch { 129 | case *tdisp: 130 | fmt.Println(fcack.TxID) 131 | case *sdisp: 132 | fmt.Println(fcack.Status) 133 | case *udisp: 134 | fmt.Println(fcack.TransactionDate) 135 | case *ddisp: 136 | fmt.Println(fcack.TransactionDateString) 137 | default: 138 | fmt.Println(fcack) 139 | } 140 | return 141 | } 142 | } 143 | 144 | fmt.Printf("Entry / transaction not found.\n") 145 | } 146 | help.Add("status", cmd) 147 | return cmd 148 | }() 149 | -------------------------------------------------------------------------------- /addchain.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "io/ioutil" 11 | "os" 12 | 13 | "github.com/FactomProject/factom" 14 | "github.com/posener/complete" 15 | ) 16 | 17 | var addchain = func() *fctCmd { 18 | cmd := new(fctCmd) 19 | cmd.helpMsg = "factom-cli addchain [-fq] [-n NAME1 -n NAME2 -h HEXNAME3" + 20 | " ] [-CET] ECADDRESS " 21 | cmd.description = "Create a new Factom Chain. Read data for the First" + 22 | " Entry from stdin. Use the Entry Credits from the specified address." + 23 | " -C ChainID. -E EntryHash. -T TxID." 24 | cmd.completion = complete.Command{ 25 | Flags: complete.Flags{ 26 | "-f": complete.PredictNothing, 27 | "-q": complete.PredictNothing, 28 | 29 | "-n": complete.PredictAnything, 30 | "-h": complete.PredictAnything, 31 | 32 | "-C": complete.PredictNothing, 33 | "-E": complete.PredictNothing, 34 | "-T": complete.PredictNothing, 35 | }, 36 | Args: predictAddress, 37 | } 38 | cmd.execFunc = func(args []string) { 39 | var ( 40 | eAcii extidsASCII 41 | eHex extidsHex 42 | ) 43 | os.Args = args 44 | exidCollector = make([][]byte, 0) 45 | flag.Var(&eAcii, "n", "Chain name element in ascii. Also is extid of"+ 46 | " First Entry") 47 | flag.Var(&eHex, "h", "Chain name element in hex. Also is extid of"+ 48 | " First Entry") 49 | fflag := flag.Bool( 50 | "f", 51 | false, 52 | "force the chain to commit and reveal without waiting on any"+ 53 | " acknowledgement checks", 54 | ) 55 | cdisp := flag.Bool("C", false, "display only the ChainID") 56 | edisp := flag.Bool("E", false, "display only the Entry Hash") 57 | tdisp := flag.Bool("T", false, "display only the TxID") 58 | qflag := flag.Bool("q", false, "quiet mode; no output") 59 | flag.Parse() 60 | args = flag.Args() 61 | 62 | if len(args) < 1 { 63 | fmt.Println(cmd.helpMsg) 64 | return 65 | } 66 | ecpub := args[0] 67 | 68 | // display normal output iff no display flags are set and quiet is unspecified 69 | display := true 70 | if *tdisp || *cdisp || *edisp || *qflag { 71 | display = false 72 | } 73 | 74 | e := new(factom.Entry) 75 | 76 | e.ExtIDs = exidCollector 77 | 78 | // Entry.Content is read from stdin 79 | if p, err := ioutil.ReadAll(os.Stdin); err != nil { 80 | errorln(err) 81 | return 82 | } else if size := len(p); size > 10240 { 83 | errorln(fmt.Sprintf("Entry of %d bytes is too large", size)) 84 | return 85 | } else { 86 | e.Content = p 87 | } 88 | 89 | // get the ec address from the wallet 90 | ec, err := factom.FetchECAddress(ecpub) 91 | if err != nil { 92 | errorln(err) 93 | return 94 | } 95 | 96 | c := factom.NewChain(e) 97 | 98 | if !*fflag { 99 | if factom.ChainExists(c.ChainID) { 100 | errorln("Chain", c.ChainID, "already exists") 101 | return 102 | } 103 | 104 | // check ec address balance 105 | balance, err := factom.GetECBalance(ecpub) 106 | if err != nil { 107 | errorln(err) 108 | return 109 | } 110 | if cost, err := factom.EntryCost(c.FirstEntry); err != nil { 111 | errorln(err) 112 | return 113 | } else if balance < int64(cost)+10 { 114 | errorln("Not enough Entry Credits") 115 | return 116 | } 117 | } 118 | 119 | // commit the chain 120 | var repeated bool 121 | txid, err := factom.CommitChain(c, ec) 122 | if err != nil { 123 | if len(err.Error()) > 15 && err.Error()[:15] != "Repeated Commit" { 124 | errorln(err) 125 | return 126 | } 127 | 128 | fmt.Println("Repeated Commit: A commit with equal or greater payment already exists, skipping commit") 129 | repeated = true 130 | } 131 | 132 | if !repeated { 133 | if display { 134 | fmt.Println("CommitTxID:", txid) 135 | } else if *tdisp { 136 | fmt.Println(txid) 137 | } 138 | 139 | if !*fflag { 140 | if _, err := waitOnCommitAck(txid); err != nil { 141 | errorln(err) 142 | return 143 | } 144 | } 145 | } 146 | 147 | // reveal chain 148 | hash, err := factom.RevealChain(c) 149 | if err != nil { 150 | errorln(err) 151 | return 152 | } 153 | if display { 154 | fmt.Println("ChainID:", c.ChainID) 155 | fmt.Println("Entryhash:", hash) 156 | } else if *cdisp { 157 | fmt.Println(c.ChainID) 158 | } else if *edisp { 159 | fmt.Println(hash) 160 | } 161 | 162 | if !*fflag { 163 | if _, err := waitOnRevealAck(hash); err != nil { 164 | errorln(err) 165 | return 166 | } 167 | } 168 | } 169 | help.Add("addchain", cmd) 170 | return cmd 171 | }() 172 | 173 | var composechain = func() *fctCmd { 174 | cmd := new(fctCmd) 175 | cmd.helpMsg = "factom-cli composechain [-f] [-n NAME1 -n NAME2" + 176 | " -h HEXNAME3 ] ECADDRESS " 177 | cmd.description = "Create API calls to create a new Factom Chain. Read" + 178 | " data for the First Entry from stdin. Use the Entry Credits from the" + 179 | " specified address." 180 | cmd.completion = complete.Command{ 181 | Flags: complete.Flags{ 182 | "-f": complete.PredictNothing, 183 | 184 | "-n": complete.PredictAnything, 185 | "-h": complete.PredictAnything, 186 | }, 187 | Args: predictAddress, 188 | } 189 | cmd.execFunc = func(args []string) { 190 | var ( 191 | eAcii extidsASCII 192 | eHex extidsHex 193 | ) 194 | os.Args = args 195 | exidCollector = make([][]byte, 0) 196 | 197 | flag.Var(&eAcii, "n", "Chain name element in ascii. Also is extid of"+ 198 | " First Entry") 199 | flag.Var(&eHex, "h", "Chain name element in hex. Also is extid of"+ 200 | " First Entry") 201 | fflag := flag.Bool( 202 | "f", 203 | false, 204 | "force the chain to commit and reveal without waiting on any"+ 205 | " acknowledgement checks", 206 | ) 207 | flag.Parse() 208 | args = flag.Args() 209 | 210 | if len(args) < 1 { 211 | fmt.Println(cmd.helpMsg) 212 | return 213 | } 214 | ecpub := args[0] 215 | 216 | e := new(factom.Entry) 217 | 218 | e.ExtIDs = exidCollector 219 | 220 | // Entry.Content is read from stdin 221 | if p, err := ioutil.ReadAll(os.Stdin); err != nil { 222 | errorln(err) 223 | return 224 | } else if size := len(p); size > 10240 { 225 | errorln(fmt.Sprintf("Entry of %d bytes is too large", size)) 226 | return 227 | } else { 228 | e.Content = p 229 | } 230 | 231 | c := factom.NewChain(e) 232 | 233 | commit, reveal, err := factom.WalletComposeChainCommitReveal(c, ecpub, *fflag) 234 | if err != nil { 235 | errorln(err) 236 | return 237 | } 238 | 239 | factomdServer := GetFactomdServer() 240 | 241 | fmt.Println( 242 | "curl -X POST --data-binary", 243 | "'"+commit.String()+"'", 244 | "-H 'content-type:text/plain;' http://"+factomdServer+"/v2", 245 | ) 246 | fmt.Println( 247 | "curl -X POST --data-binary", 248 | "'"+reveal.String()+"'", 249 | "-H 'content-type:text/plain;' http://"+factomdServer+"/v2", 250 | ) 251 | 252 | } 253 | help.Add("composechain", cmd) 254 | return cmd 255 | }() 256 | -------------------------------------------------------------------------------- /addentry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "io/ioutil" 11 | "os" 12 | 13 | "github.com/FactomProject/factom" 14 | "github.com/posener/complete" 15 | ) 16 | 17 | var addentry = func() *fctCmd { 18 | cmd := new(fctCmd) 19 | cmd.helpMsg = "factom-cli addentry [-fq] [-n NAME1 -h HEXNAME2" + 20 | " ...|-c CHAINID] [-e EXTID1 -e EXTID2 -x HEXEXTID ...] [-CET]" + 21 | " ECADDRESS " 22 | cmd.description = "Create a new Factom Entry. Read data for the Entry" + 23 | " from stdin. Use the Entry Credits from the specified address." + 24 | " -C ChainID. -E EntryHash. -T TxID." 25 | cmd.completion = complete.Command{ 26 | Flags: complete.Flags{ 27 | "-f": complete.PredictNothing, 28 | "-q": complete.PredictNothing, 29 | 30 | "-n": complete.PredictAnything, 31 | "-h": complete.PredictAnything, 32 | 33 | "-C": complete.PredictNothing, 34 | "-E": complete.PredictNothing, 35 | "-T": complete.PredictNothing, 36 | }, 37 | Args: predictAddress, 38 | } 39 | cmd.execFunc = func(args []string) { 40 | os.Args = args 41 | var ( 42 | cid = flag.String("c", "", "hex encoded chainid for the entry") 43 | eAcii extidsASCII 44 | eHex extidsHex 45 | nAcii namesASCII 46 | nHex namesHex 47 | ) 48 | 49 | // -e -x extids 50 | exidCollector = make([][]byte, 0) 51 | flag.Var(&eAcii, "e", "external id for the entry in ascii") 52 | flag.Var(&eHex, "x", "external id for the entry in hex") 53 | 54 | // -n -h names 55 | nameCollector = make([][]byte, 0) 56 | flag.Var(&nAcii, "n", "ascii name element") 57 | flag.Var(&nHex, "h", "hex binary name element") 58 | 59 | // -f force 60 | fflag := flag.Bool( 61 | "f", 62 | false, 63 | "force the entry to commit and reveal without waiting on any"+ 64 | " acknowledgement checks", 65 | ) 66 | 67 | // -CET display flags 68 | cdisp := flag.Bool("C", false, "display only the ChainID") 69 | edisp := flag.Bool("E", false, "display only the Entry Hash") 70 | tdisp := flag.Bool("T", false, "display only the TxID") 71 | 72 | // -q quiet flags 73 | qflag := flag.Bool("q", false, "quiet mode; no output") 74 | 75 | flag.Parse() 76 | args = flag.Args() 77 | 78 | if len(args) < 1 { 79 | fmt.Println(cmd.helpMsg) 80 | return 81 | } 82 | ecpub := args[0] 83 | 84 | // display normal output iff no display flags are set and quiet is unspecified 85 | display := true 86 | if *cdisp || *edisp || *tdisp || *qflag { 87 | display = false 88 | } 89 | 90 | e := new(factom.Entry) 91 | 92 | // set the chainid from -c or from -n -h 93 | if *cid != "" { 94 | e.ChainID = *cid 95 | } else if len(nameCollector) != 0 { 96 | e.ChainID = nametoid(nameCollector) 97 | } else { 98 | fmt.Println(cmd.helpMsg) 99 | return 100 | } 101 | 102 | e.ExtIDs = exidCollector 103 | 104 | // Entry.Content is read from stdin 105 | if p, err := ioutil.ReadAll(os.Stdin); err != nil { 106 | errorln(err) 107 | return 108 | } else if size := len(p); size > 10240 { 109 | errorln(fmt.Sprintf("Entry of %d bytes is too large", size)) 110 | return 111 | } else { 112 | e.Content = p 113 | } 114 | 115 | // get the ec address from the wallet 116 | ec, err := factom.FetchECAddress(ecpub) 117 | if err != nil { 118 | errorln(err) 119 | return 120 | } 121 | 122 | if !*fflag { 123 | if !factom.ChainExists(e.ChainID) { 124 | errorln("Chain", e.ChainID, "was not found") 125 | return 126 | } 127 | 128 | // check ec address balance 129 | balance, err := factom.GetECBalance(ecpub) 130 | if err != nil { 131 | errorln(err) 132 | return 133 | } 134 | if cost, err := factom.EntryCost(e); err != nil { 135 | errorln(err) 136 | return 137 | } else if balance < int64(cost) { 138 | errorln("Not enough Entry Credits") 139 | return 140 | } 141 | } 142 | 143 | // commit entry 144 | var repeated bool 145 | txid, err := factom.CommitEntry(e, ec) 146 | if err != nil { 147 | if len(err.Error()) > 15 && err.Error()[:15] != "Repeated Commit" { 148 | errorln(err) 149 | return 150 | } 151 | 152 | fmt.Println("Repeated Commit: A commit with equal or greater payment already exists, skipping commit") 153 | repeated = true 154 | } 155 | 156 | if !repeated { 157 | if display { 158 | fmt.Println("CommitTxID:", txid) 159 | } else if *tdisp { 160 | fmt.Println(txid) 161 | } 162 | 163 | if !*fflag { 164 | if _, err := waitOnCommitAck(txid); err != nil { 165 | errorln(err) 166 | return 167 | } 168 | } 169 | } 170 | // reveal entry 171 | hash, err := factom.RevealEntry(e) 172 | if err != nil { 173 | errorln(err) 174 | return 175 | } 176 | if !*fflag { 177 | if _, err := waitOnRevealAck(hash); err != nil { 178 | errorln(err) 179 | return 180 | } 181 | } 182 | if display { 183 | fmt.Println("ChainID:", e.ChainID) 184 | fmt.Println("Entryhash:", hash) 185 | } else if *cdisp { 186 | fmt.Println(e.ChainID) 187 | } else if *edisp { 188 | fmt.Println(hash) 189 | } 190 | 191 | } 192 | help.Add("addentry", cmd) 193 | return cmd 194 | }() 195 | 196 | var composeentry = func() *fctCmd { 197 | cmd := new(fctCmd) 198 | cmd.helpMsg = "factom-cli composeentry [-f] [-n NAME1 -h HEXNAME2" + 199 | " ...|-c CHAINID] [-e EXTID1 -e EXTID2 -x HEXEXTID ...] ECADDRESS" + 200 | " " 201 | cmd.description = "Create API calls to create a new Factom Entry. Read" + 202 | " data for the Entry from stdin. Use the Entry Credits from the" + 203 | " specified address." 204 | cmd.completion = complete.Command{ 205 | Flags: complete.Flags{ 206 | "-f": complete.PredictNothing, 207 | 208 | "-c": complete.PredictAnything, 209 | "-e": complete.PredictAnything, 210 | "-x": complete.PredictAnything, 211 | "-n": complete.PredictAnything, 212 | "-h": complete.PredictAnything, 213 | }, 214 | Args: predictAddress, 215 | } 216 | cmd.execFunc = func(args []string) { 217 | os.Args = args 218 | var ( 219 | cid = flag.String("c", "", "hex encoded chainid for the entry") 220 | eAcii extidsASCII 221 | eHex extidsHex 222 | nAcii namesASCII 223 | nHex namesHex 224 | ) 225 | 226 | // -e -x extids 227 | exidCollector = make([][]byte, 0) 228 | flag.Var(&eAcii, "e", "external id for the entry in ascii") 229 | flag.Var(&eHex, "x", "external id for the entry in hex") 230 | 231 | // -n -h names 232 | nameCollector = make([][]byte, 0) 233 | flag.Var(&nAcii, "n", "ascii name element") 234 | flag.Var(&nHex, "h", "hex binary name element") 235 | 236 | // -f force 237 | fflag := flag.Bool( 238 | "f", 239 | false, 240 | "force the entry to commit and reveal without waiting on any"+ 241 | " acknowledgement checks", 242 | ) 243 | 244 | flag.Parse() 245 | args = flag.Args() 246 | 247 | if len(args) < 1 { 248 | fmt.Println(cmd.helpMsg) 249 | return 250 | } 251 | ecpub := args[0] 252 | 253 | e := new(factom.Entry) 254 | 255 | // set the chainid from -c or from -n -h 256 | if *cid != "" { 257 | e.ChainID = *cid 258 | } else if len(nameCollector) != 0 { 259 | e.ChainID = nametoid(nameCollector) 260 | } else { 261 | fmt.Println(cmd.helpMsg) 262 | return 263 | } 264 | 265 | e.ExtIDs = exidCollector 266 | 267 | // Entry.Content is read from stdin 268 | if p, err := ioutil.ReadAll(os.Stdin); err != nil { 269 | errorln(err) 270 | return 271 | } else if size := len(p); size > 10240 { 272 | errorln(fmt.Sprintf("Entry of %d bytes is too large", size)) 273 | return 274 | } else { 275 | e.Content = p 276 | } 277 | 278 | commit, reveal, err := factom.WalletComposeEntryCommitReveal(e, ecpub, *fflag) 279 | if err != nil { 280 | errorln(err) 281 | return 282 | } 283 | 284 | factomdServer := GetFactomdServer() 285 | 286 | fmt.Println( 287 | "curl -X POST --data-binary", 288 | "'"+commit.String()+"'", 289 | "-H 'content-type:text/plain;' http://"+factomdServer+"/v2", 290 | ) 291 | fmt.Println( 292 | "curl -X POST --data-binary", 293 | "'"+reveal.String()+"'", 294 | "-H 'content-type:text/plain;' http://"+factomdServer+"/v2", 295 | ) 296 | } 297 | help.Add("composeentry", cmd) 298 | return cmd 299 | }() 300 | -------------------------------------------------------------------------------- /addresses.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | 12 | "github.com/FactomProject/factom" 13 | "github.com/posener/complete" 14 | ) 15 | 16 | // balance prints the current balance of the specified address 17 | var balance = func() *fctCmd { 18 | cmd := new(fctCmd) 19 | cmd.helpMsg = "factom-cli balance [-r] ADDRESS" 20 | cmd.description = "If this is an EC Address, returns number of Entry Credits. If this is a Factoid Address, returns the Factoid balance." 21 | cmd.completion = complete.Command{ 22 | Flags: complete.Flags{ 23 | "-r": complete.PredictNothing, 24 | }, 25 | Args: predictAddress, 26 | } 27 | cmd.execFunc = func(args []string) { 28 | os.Args = args 29 | var res = flag.Bool("r", false, "resolve dns address") 30 | flag.Parse() 31 | args = flag.Args() 32 | 33 | if len(args) < 1 { 34 | fmt.Println(cmd.helpMsg) 35 | return 36 | } 37 | addr := args[0] 38 | 39 | switch factom.AddressStringType(addr) { 40 | case factom.FactoidPub: 41 | b, err := factom.GetFactoidBalance(addr) 42 | if err != nil { 43 | errorln(err) 44 | } else { 45 | fmt.Println(factoshiToFactoid(b)) 46 | } 47 | return 48 | case factom.ECPub: 49 | c, err := factom.GetECBalance(addr) 50 | if err != nil { 51 | errorln(err) 52 | } else { 53 | fmt.Println(c) 54 | } 55 | return 56 | } 57 | 58 | // if -r flag is present, resolve dns address then get the fct and ec 59 | // blance 60 | if *res { 61 | f, e, err := factom.GetDnsBalance(addr) 62 | if err != nil { 63 | fmt.Println(err) 64 | return 65 | } 66 | fmt.Println(addr, "fct", factoshiToFactoid(f)) 67 | fmt.Println(addr, "ec", e) 68 | } else { 69 | fmt.Println("Undefined or invalid address") 70 | } 71 | } 72 | help.Add("balance", cmd) 73 | return cmd 74 | }() 75 | 76 | // balancetotals shows the total balance of all of the Factoid and Entry Credts 77 | // in the wallet 78 | var balancetotals = func() *fctCmd { 79 | cmd := new(fctCmd) 80 | cmd.helpMsg = "factom-cli balancetotals [-FS -FA -ES -EA]" 81 | cmd.description = "This is the total number of Factoids and Entry Credits in the wallet" 82 | cmd.completion = complete.Command{ 83 | Flags: complete.Flags{ 84 | "-FS": complete.PredictNothing, 85 | "-FA": complete.PredictNothing, 86 | "-ES": complete.PredictNothing, 87 | "-EA": complete.PredictNothing, 88 | }, 89 | } 90 | cmd.execFunc = func(args []string) { 91 | os.Args = args 92 | var fsdisp = flag.Bool("FS", false, "Display only the savedFCT value") 93 | var fadisp = flag.Bool("FA", false, "Display only the ackFCT value") 94 | var esdisp = flag.Bool("ES", false, "Display only the savedEC value") 95 | var eadisp = flag.Bool("EA", false, "Display only the ackEC value") 96 | flag.Parse() 97 | args = flag.Args() 98 | 99 | fs, fa, es, ea, err := factom.GetBalanceTotals() 100 | if err != nil { 101 | errorln(err) 102 | return 103 | } 104 | 105 | switch { 106 | case *fsdisp: 107 | fmt.Println(factoshiToFactoid(fs)) 108 | case *fadisp: 109 | fmt.Println(factoshiToFactoid(fa)) 110 | case *esdisp: 111 | fmt.Println(es) 112 | case *eadisp: 113 | fmt.Println(ea) 114 | default: 115 | fmt.Println("savedFCT:", factoshiToFactoid(fs)) 116 | fmt.Println("ackFCT:", factoshiToFactoid(fa)) 117 | fmt.Println("savedEC:", es) 118 | fmt.Println("ackEC:", ea) 119 | } 120 | } 121 | help.Add("balancetotals", cmd) 122 | return cmd 123 | }() 124 | 125 | // ecrate shows the entry credit conversion rate in factoids 126 | var ecrate = func() *fctCmd { 127 | cmd := new(fctCmd) 128 | cmd.helpMsg = "factom-cli ecrate" 129 | cmd.description = "It takes this many Factoids to buy an Entry Credit. Displays the larger between current and future rates. Also used to set Factoid fees." 130 | cmd.execFunc = func(args []string) { 131 | rate, err := factom.GetECRate() 132 | if err != nil { 133 | errorln(err) 134 | return 135 | } 136 | fmt.Println(factoshiToFactoid(rate)) 137 | 138 | } 139 | help.Add("ecrate", cmd) 140 | return cmd 141 | }() 142 | 143 | // exportaddresses lists the private addresses from the wallet 144 | var exportaddresses = func() *fctCmd { 145 | cmd := new(fctCmd) 146 | cmd.helpMsg = "factom-cli exportaddresses" 147 | cmd.description = "List the private addresses stored in the wallet" 148 | cmd.execFunc = func(args []string) { 149 | os.Args = args 150 | flag.Parse() 151 | args = flag.Args() 152 | 153 | fs, es, err := factom.FetchAddresses() 154 | if err != nil { 155 | errorln(err) 156 | return 157 | } 158 | for _, a := range fs { 159 | fmt.Println(a.SecString(), a.String()) 160 | } 161 | for _, a := range es { 162 | fmt.Println(a.SecString(), a.String()) 163 | } 164 | } 165 | help.Add("exportaddresses", cmd) 166 | return cmd 167 | }() 168 | 169 | // importaddresses imports addresses from 1 or more secret keys into the wallet 170 | var importaddresses = func() *fctCmd { 171 | cmd := new(fctCmd) 172 | cmd.helpMsg = "factom-cli importaddress ADDRESS [ADDRESS...]" 173 | cmd.description = "Import one or more secret keys into the wallet" 174 | cmd.execFunc = func(args []string) { 175 | os.Args = args 176 | flag.Parse() 177 | args = flag.Args() 178 | 179 | if len(args) < 1 { 180 | fmt.Println(cmd.helpMsg) 181 | return 182 | } 183 | fs, es, err := factom.ImportAddresses(args...) 184 | if err != nil { 185 | errorln(err) 186 | return 187 | } 188 | for _, a := range fs { 189 | fmt.Println(a) 190 | } 191 | for _, a := range es { 192 | fmt.Println(a) 193 | } 194 | } 195 | help.Add("importaddress", cmd) 196 | return cmd 197 | }() 198 | 199 | var importkoinify = func() *fctCmd { 200 | cmd := new(fctCmd) 201 | cmd.helpMsg = "factom-cli importkoinify '12WORDS'" 202 | cmd.description = "Import 12 words from Koinify sale into the Wallet" 203 | cmd.execFunc = func(args []string) { 204 | os.Args = args 205 | flag.Parse() 206 | args = flag.Args() 207 | 208 | if len(args) < 1 || len(args) > 1 { 209 | fmt.Println(cmd.helpMsg, " Note, 12 words must be in quotes") 210 | return 211 | } 212 | f, err := factom.ImportKoinify(args[0]) 213 | if err != nil { 214 | errorln(err) 215 | return 216 | } 217 | fmt.Println(f) 218 | } 219 | help.Add("importkoinify", cmd) 220 | return cmd 221 | }() 222 | 223 | // newecaddress generates a new ec address in the wallet 224 | var newecaddress = func() *fctCmd { 225 | cmd := new(fctCmd) 226 | cmd.helpMsg = "factom-cli newecaddress" 227 | cmd.description = "Generate a new Entry Credit Address in the wallet" 228 | cmd.execFunc = func(args []string) { 229 | os.Args = args 230 | flag.Parse() 231 | args = flag.Args() 232 | 233 | a, err := factom.GenerateECAddress() 234 | if err != nil { 235 | errorln(err) 236 | return 237 | } 238 | fmt.Println(a) 239 | } 240 | help.Add("newecaddress", cmd) 241 | return cmd 242 | }() 243 | 244 | // newfctaddress generates a new ec address in the wallet 245 | var newfctaddress = func() *fctCmd { 246 | cmd := new(fctCmd) 247 | cmd.helpMsg = "factom-cli newfctaddress" 248 | cmd.description = "Generate a new Factoid Address in the wallet" 249 | cmd.execFunc = func(args []string) { 250 | os.Args = args 251 | flag.Parse() 252 | args = flag.Args() 253 | 254 | a, err := factom.GenerateFactoidAddress() 255 | if err != nil { 256 | errorln(err) 257 | return 258 | } 259 | fmt.Println(a) 260 | } 261 | help.Add("newfctaddress", cmd) 262 | return cmd 263 | }() 264 | 265 | // listaddresses lists the addresses in the wallet 266 | var listaddresses = func() *fctCmd { 267 | cmd := new(fctCmd) 268 | cmd.helpMsg = "factom-cli listaddresses" 269 | cmd.description = "List the addresses stored in the wallet" 270 | cmd.execFunc = func(args []string) { 271 | os.Args = args 272 | adisp := flag.Bool( 273 | "A", 274 | false, 275 | "display only the address without looking up the balance", 276 | ) 277 | flag.Parse() 278 | args = flag.Args() 279 | 280 | fs, es, err := factom.FetchAddresses() 281 | if err != nil { 282 | errorln(err) 283 | return 284 | } 285 | 286 | if *adisp { 287 | for _, a := range fs { 288 | fmt.Println(a) 289 | } 290 | for _, a := range es { 291 | fmt.Println(a) 292 | } 293 | } else { 294 | for _, a := range fs { 295 | b, err := factom.GetFactoidBalance(a.String()) 296 | if err != nil { 297 | errorln(err) 298 | fmt.Println(a) 299 | } else { 300 | fmt.Println(a, factoshiToFactoid(b)) 301 | } 302 | } 303 | for _, a := range es { 304 | c, err := factom.GetECBalance(a.String()) 305 | if err != nil { 306 | errorln(err) 307 | fmt.Println(a) 308 | } else { 309 | fmt.Println(a, c) 310 | } 311 | } 312 | } 313 | } 314 | help.Add("listaddresses", cmd) 315 | return cmd 316 | }() 317 | 318 | // Removes an address 319 | var removeAddress = func() *fctCmd { 320 | cmd := new(fctCmd) 321 | cmd.helpMsg = "factom-cli rmaddress ADDRESS" 322 | cmd.description = "Removes the public and private key from the wallet for the address specified." 323 | cmd.completion = complete.Command{ 324 | Args: predictAddress, 325 | } 326 | cmd.execFunc = func(args []string) { 327 | if len(args) < 2 { 328 | fmt.Println(cmd.helpMsg) 329 | return 330 | } 331 | addr := args[1] 332 | 333 | err := factom.RemoveAddress(addr) 334 | if err != nil { 335 | fmt.Printf("%v\n", err) 336 | } 337 | } 338 | help.Add("rmaddress", cmd) 339 | return cmd 340 | }() 341 | -------------------------------------------------------------------------------- /byheight.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "flag" 11 | "fmt" 12 | "os" 13 | "strconv" 14 | 15 | "github.com/FactomProject/factom" 16 | ) 17 | 18 | // Abheight fetches the GetBlockByHeightRaw data for admin block. 19 | // Deprecated: should use ablock. 20 | var Abheight = func() *fctCmd { 21 | var supressRawData string 22 | cmd := new(fctCmd) 23 | cmd.helpMsg = "factom-cli get abheight HEIGHT -r (to suppress Raw Data)" 24 | cmd.description = "Get Admin Block by height" 25 | cmd.execFunc = func(args []string) { 26 | os.Args = args 27 | flag.Parse() 28 | args = flag.Args() 29 | 30 | if len(args) < 1 { 31 | fmt.Println(cmd.helpMsg) 32 | return 33 | } 34 | h := args[0] 35 | height, err := strconv.ParseInt(h, 0, 64) 36 | if err != nil { 37 | fmt.Println(cmd.helpMsg) 38 | return 39 | } 40 | 41 | if len(args) > 1 { 42 | supressRawData = args[1] 43 | } 44 | 45 | resp, err := factom.GetBlockByHeightRaw("a", height) 46 | if err != nil { 47 | errorln(err) 48 | return 49 | } 50 | 51 | if supressRawData == "-r" { 52 | resp.RawData = "" 53 | } 54 | 55 | data, err := json.Marshal(resp) 56 | if err != nil { 57 | errorln(err) 58 | return 59 | } 60 | 61 | var out bytes.Buffer 62 | json.Indent(&out, data, "", "\t") 63 | 64 | fmt.Printf("%s\n", out.Bytes()) 65 | } 66 | help.Add("get abheight", cmd) 67 | return cmd 68 | }() 69 | 70 | // Dbheight fetches the GetBlockByHeightRaw data for Directory block. 71 | // Deprecated: should use dblock 72 | var Dbheight = func() *fctCmd { 73 | var supressRawData string 74 | cmd := new(fctCmd) 75 | cmd.helpMsg = "factom-cli get dbheight HEIGHT -r (to suppress Raw Data)" 76 | cmd.description = "Get Directory Block by height" 77 | cmd.execFunc = func(args []string) { 78 | os.Args = args 79 | flag.Parse() 80 | args = flag.Args() 81 | 82 | if len(args) < 1 { 83 | fmt.Println(cmd.helpMsg) 84 | return 85 | } 86 | h := args[0] 87 | height, err := strconv.ParseInt(h, 0, 64) 88 | if err != nil { 89 | fmt.Println(cmd.helpMsg) 90 | return 91 | } 92 | 93 | if len(args) > 1 { 94 | supressRawData = args[1] 95 | } 96 | 97 | resp, err := factom.GetBlockByHeightRaw("d", height) 98 | if err != nil { 99 | errorln(err) 100 | return 101 | } 102 | 103 | if supressRawData == "-r" { 104 | resp.RawData = "" 105 | } 106 | 107 | data, err := json.Marshal(resp) 108 | if err != nil { 109 | errorln(err) 110 | return 111 | } 112 | 113 | var out bytes.Buffer 114 | json.Indent(&out, data, "", "\t") 115 | 116 | fmt.Printf("%s\n", out.Bytes()) 117 | } 118 | help.Add("get dbheight", cmd) 119 | return cmd 120 | }() 121 | 122 | // Ecbheight fetches the GetBlockByHeightRaw data for Entry Credit Block. 123 | // Deprecated: should use ecblock 124 | var Ecbheight = func() *fctCmd { 125 | var supressRawData string 126 | cmd := new(fctCmd) 127 | cmd.helpMsg = "factom-cli get ecbheight HEIGHT -r (to suppress Raw Data)" 128 | cmd.description = "Get Entry Credit Block by height" 129 | cmd.execFunc = func(args []string) { 130 | os.Args = args 131 | flag.Parse() 132 | args = flag.Args() 133 | 134 | if len(args) < 1 { 135 | fmt.Println(cmd.helpMsg) 136 | return 137 | } 138 | h := args[0] 139 | height, err := strconv.ParseInt(h, 0, 64) 140 | if err != nil { 141 | fmt.Println(cmd.helpMsg) 142 | return 143 | } 144 | 145 | if len(args) > 1 { 146 | supressRawData = args[1] 147 | } 148 | 149 | resp, err := factom.GetBlockByHeightRaw("ec", height) 150 | if err != nil { 151 | errorln(err) 152 | return 153 | } 154 | 155 | if supressRawData == "-r" { 156 | resp.RawData = "" 157 | } 158 | 159 | data, err := json.Marshal(resp) 160 | if err != nil { 161 | errorln(err) 162 | return 163 | } 164 | 165 | var out bytes.Buffer 166 | json.Indent(&out, data, "", "\t") 167 | 168 | fmt.Printf("%s\n", out.Bytes()) 169 | } 170 | help.Add("get ecbheight", cmd) 171 | return cmd 172 | }() 173 | 174 | // Fbheight fetches the GetBlockByHeightRaw data for Factoid Block. 175 | // Deprecated: should use fblock. 176 | var Fbheight = func() *fctCmd { 177 | var supressRawData string 178 | cmd := new(fctCmd) 179 | cmd.helpMsg = "factom-cli get fbheight HEIGHT -r (to suppress Raw Data)" 180 | cmd.description = "Get Factoid Block by height" 181 | cmd.execFunc = func(args []string) { 182 | os.Args = args 183 | flag.Parse() 184 | args = flag.Args() 185 | 186 | if len(args) < 1 { 187 | fmt.Println(cmd.helpMsg) 188 | return 189 | } 190 | h := args[0] 191 | height, err := strconv.ParseInt(h, 0, 64) 192 | if err != nil { 193 | fmt.Println(cmd.helpMsg) 194 | return 195 | } 196 | 197 | if len(args) > 1 { 198 | supressRawData = args[1] 199 | } 200 | 201 | resp, err := factom.GetBlockByHeightRaw("f", height) 202 | if err != nil { 203 | errorln(err) 204 | return 205 | } 206 | 207 | if supressRawData == "-r" { 208 | resp.RawData = "" 209 | } 210 | 211 | data, err := json.Marshal(resp) 212 | if err != nil { 213 | errorln(err) 214 | return 215 | } 216 | 217 | var out bytes.Buffer 218 | json.Indent(&out, data, "", "\t") 219 | 220 | fmt.Printf("%s\n", out.Bytes()) 221 | } 222 | help.Add("get fbheight", cmd) 223 | return cmd 224 | }() 225 | -------------------------------------------------------------------------------- /completion.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/FactomProject/factom" 9 | "github.com/posener/complete" 10 | ) 11 | 12 | // predictTxName creates command completions from the tx names in 13 | // factom-walletd. It predicts nothing if there is no wallet running. 14 | var predictTxName = complete.PredictFunc(func(args complete.Args) []string { 15 | txs, err := factom.ListTransactionsTmp() 16 | if err != nil { 17 | return nil 18 | } 19 | 20 | s := make([]string, 0) 21 | for _, tx := range txs { 22 | s = append(s, tx.Name) 23 | } 24 | return s 25 | }) 26 | 27 | // predictAddress creates command completions from the addresses in 28 | // factom-walletd. It predicts nothing if there is not wallet running. 29 | var predictAddress = complete.PredictFunc(func(args complete.Args) []string { 30 | fcs, ecs, err := factom.FetchAddresses() 31 | if err != nil { 32 | return nil 33 | } 34 | 35 | s := make([]string, 0) 36 | for _, fc := range fcs { 37 | s = append(s, fc.String()) 38 | } 39 | for _, ec := range ecs { 40 | s = append(s, ec.String()) 41 | } 42 | return s 43 | }) 44 | 45 | var predictIdentityKey = complete.PredictFunc(func(args complete.Args) []string { 46 | ks, err := factom.FetchIdentityKeys() 47 | if err != nil { 48 | return nil 49 | } 50 | 51 | s := make([]string, 0) 52 | for _, k := range ks { 53 | s = append(s, k.String()) 54 | } 55 | return s 56 | }) 57 | -------------------------------------------------------------------------------- /diagnostics.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | 12 | "github.com/FactomProject/cli" 13 | "github.com/FactomProject/factom" 14 | "github.com/posener/complete" 15 | ) 16 | 17 | var diagnostics = func() *fctCmd { 18 | cmd := new(fctCmd) 19 | cmd.helpMsg = "factom-cli diagnostics [server|network|sync|election|authset]" 20 | cmd.description = "Get diagnostic information about the Factom network" 21 | cmd.completion = complete.Command{ 22 | Sub: complete.Commands{ 23 | "server": complete.Command{}, 24 | "network": complete.Command{}, 25 | "sync": complete.Command{}, 26 | "election": complete.Command{}, 27 | "authset": complete.Command{}, 28 | }, 29 | } 30 | cmd.execFunc = func(args []string) { 31 | os.Args = args 32 | flag.Parse() 33 | args = flag.Args() 34 | 35 | c := cli.New() 36 | c.Handle("server", diagnosticsServer) 37 | c.Handle("network", diagnosticsNetwork) 38 | c.Handle("sync", diagnosticsSync) 39 | c.Handle("election", diagnosticsElection) 40 | c.Handle("authset", diagnosticsAuthSet) 41 | c.HandleDefaultFunc(func(args []string) { 42 | if len(args) != 0 { 43 | fmt.Println(cmd.helpMsg) 44 | return 45 | } 46 | d, err := factom.GetDiagnostics() 47 | if err != nil { 48 | errorln(err) 49 | return 50 | } 51 | fmt.Println(d) 52 | }) 53 | c.Execute(args) 54 | } 55 | help.Add("diagnostics", cmd) 56 | return cmd 57 | }() 58 | 59 | var diagnosticsServer = func() *fctCmd { 60 | cmd := new(fctCmd) 61 | cmd.helpMsg = "factom-cli diagnostics server [-NIKR]" 62 | cmd.description = "Get diagnostic information about the Factom API server" 63 | cmd.execFunc = func(args []string) { 64 | os.Args = args 65 | ndisp := flag.Bool("N", false, "display only the name of the API server") 66 | idisp := flag.Bool("I", false, "display only the ID of the API server") 67 | kdisp := flag.Bool("K", false, "display only the public key of the API server") 68 | rdisp := flag.Bool("R", false, "display only the role of the API server") 69 | flag.Parse() 70 | args = flag.Args() 71 | 72 | d, err := factom.GetDiagnostics() 73 | if err != nil { 74 | errorln(err) 75 | return 76 | } 77 | 78 | switch { 79 | case *ndisp: 80 | fmt.Println(d.Name) 81 | case *idisp: 82 | fmt.Println(d.ID) 83 | case *kdisp: 84 | fmt.Println(d.PublicKey) 85 | case *rdisp: 86 | fmt.Println(d.Role) 87 | default: 88 | fmt.Println("Name:", d.Name) 89 | fmt.Println("ID:", d.ID) 90 | fmt.Println("PublicKey:", d.PublicKey) 91 | fmt.Println("Role:", d.Role) 92 | } 93 | } 94 | help.Add("diagnostics server", cmd) 95 | return cmd 96 | }() 97 | 98 | var diagnosticsNetwork = func() *fctCmd { 99 | cmd := new(fctCmd) 100 | cmd.helpMsg = "factom-cli diagnostics network [-LMDPHTB]" 101 | cmd.description = "Get diagnostic information about the Factom network" 102 | cmd.execFunc = func(args []string) { 103 | os.Args = args 104 | ldisp := flag.Bool("L", false, "display only the Leader height") 105 | mdisp := flag.Bool("M", false, "display only the current minute") 106 | ddisp := flag.Bool("D", false, "display only the current minute duration") 107 | pdisp := flag.Bool("P", false, "display only the previous minute duration") 108 | hdisp := flag.Bool("H", false, "display only the balance hash") 109 | tdisp := flag.Bool("T", false, "display only the temporary balance hash") 110 | bdisp := flag.Bool("B", false, "display only the last block from the DBState") 111 | flag.Parse() 112 | args = flag.Args() 113 | 114 | d, err := factom.GetDiagnostics() 115 | if err != nil { 116 | errorln(err) 117 | return 118 | } 119 | 120 | switch { 121 | case *ldisp: 122 | fmt.Println(d.LeaderHeight) 123 | case *mdisp: 124 | fmt.Println(d.CurrentMinute) 125 | case *ddisp: 126 | fmt.Println(d.CurrentMinuteDuration) 127 | case *pdisp: 128 | fmt.Println(d.PrevMinuteDuration) 129 | case *hdisp: 130 | fmt.Println(d.BalanceHash) 131 | case *tdisp: 132 | fmt.Println(d.TempBalanceHash) 133 | case *bdisp: 134 | fmt.Println(d.LastBlockFromDBState) 135 | default: 136 | fmt.Println("LeaderHeight:", d.LeaderHeight) 137 | fmt.Println("CurrentMinute:", d.CurrentMinute) 138 | fmt.Println("CurrentMinuteDuration:", d.CurrentMinuteDuration) 139 | fmt.Println("PrevMinuteDuration:", d.PrevMinuteDuration) 140 | fmt.Println("BalanceHash:", d.BalanceHash) 141 | fmt.Println("TempBalanceHash:", d.TempBalanceHash) 142 | fmt.Println("LastBlockFromDBState:", d.LastBlockFromDBState) 143 | } 144 | } 145 | help.Add("diagnostics network", cmd) 146 | return cmd 147 | }() 148 | 149 | var diagnosticsSync = func() *fctCmd { 150 | cmd := new(fctCmd) 151 | cmd.helpMsg = "factom-cli diagnostics sync [-SRE]" 152 | cmd.description = "Get diagnostic information about the network syncing" 153 | cmd.execFunc = func(args []string) { 154 | os.Args = args 155 | sdisp := flag.Bool("S", false, "display only the syncing status") 156 | rdisp := flag.Bool("R", false, "display only the received status") 157 | edisp := flag.Bool("E", false, "display only the expected status") 158 | flag.Parse() 159 | args = flag.Args() 160 | 161 | d, err := factom.GetDiagnostics() 162 | if err != nil { 163 | errorln(err) 164 | return 165 | } 166 | 167 | switch { 168 | case *sdisp: 169 | fmt.Println(d.SyncInfo.Status) 170 | case *rdisp: 171 | fmt.Println(d.SyncInfo.Received) 172 | case *edisp: 173 | fmt.Println(d.SyncInfo.Expected) 174 | default: 175 | fmt.Println("Status:", d.SyncInfo.Status) 176 | fmt.Println("Received:", d.SyncInfo.Received) 177 | fmt.Println("Expected:", d.SyncInfo.Expected) 178 | for _, m := range d.SyncInfo.Missing { 179 | fmt.Println("Missing:", m) 180 | } 181 | } 182 | } 183 | help.Add("diagnostics sync", cmd) 184 | return cmd 185 | }() 186 | 187 | var diagnosticsElection = func() *fctCmd { 188 | cmd := new(fctCmd) 189 | cmd.helpMsg = "factom-cli diagnostics election [-PVFIR]" 190 | cmd.description = "Get diagnostic information about the Factom network election process" 191 | cmd.execFunc = func(args []string) { 192 | os.Args = args 193 | pdisp := flag.Bool("P", false, "display only the progress status") 194 | vdisp := flag.Bool("V", false, "display only the VM Index") 195 | fdisp := flag.Bool("F", false, "display only the Federated Index") 196 | idisp := flag.Bool("I", false, "display only the Federated ID") 197 | rdisp := flag.Bool("R", false, "display only the current round") 198 | flag.Parse() 199 | args = flag.Args() 200 | 201 | d, err := factom.GetDiagnostics() 202 | if err != nil { 203 | errorln(err) 204 | return 205 | } 206 | 207 | switch { 208 | case *pdisp: 209 | fmt.Println(d.ElectionInfo.InProgress) 210 | case *vdisp: 211 | fmt.Println(d.ElectionInfo.VMIndex) 212 | case *fdisp: 213 | fmt.Println(d.ElectionInfo.FedIndex) 214 | case *idisp: 215 | fmt.Println(d.ElectionInfo.FedID) 216 | case *rdisp: 217 | fmt.Println(d.ElectionInfo.Round) 218 | default: 219 | fmt.Println("InProgress:", d.ElectionInfo.InProgress) 220 | fmt.Println("VMIndex:", d.ElectionInfo.VMIndex) 221 | fmt.Println("FedIndex:", d.ElectionInfo.FedIndex) 222 | fmt.Println("FedID:", d.ElectionInfo.FedID) 223 | fmt.Println("Round:", d.ElectionInfo.Round) 224 | } 225 | } 226 | help.Add("diagnostics election", cmd) 227 | return cmd 228 | }() 229 | 230 | var diagnosticsAuthSet = func() *fctCmd { 231 | cmd := new(fctCmd) 232 | cmd.helpMsg = "factom-cli diagnostics authset" 233 | cmd.description = "Get diagnostic information about the Factom authorized servers" 234 | cmd.execFunc = func(args []string) { 235 | os.Args = args 236 | flag.Parse() 237 | args = flag.Args() 238 | 239 | d, err := factom.GetDiagnostics() 240 | if err != nil { 241 | errorln(err) 242 | return 243 | } 244 | 245 | fmt.Println("Leaders {") 246 | for _, v := range d.AuthSet.Leaders { 247 | fmt.Println(" ID:", v.ID) 248 | fmt.Println(" VM:", v.VM) 249 | fmt.Println(" ProcessListHeight:", v.ProcessListHeight) 250 | fmt.Println(" ListLength:", v.ListLength) 251 | fmt.Println(" NextNil:", v.NextNil) 252 | } 253 | fmt.Println("}") // Leaders 254 | fmt.Println("Audits {") 255 | for _, v := range d.AuthSet.Audits { 256 | fmt.Println(" ID:", v.ID) 257 | fmt.Println(" Online:", v.Online) 258 | } 259 | fmt.Println("}") // Audits 260 | } 261 | help.Add("diagnostics authset", cmd) 262 | return cmd 263 | }() 264 | -------------------------------------------------------------------------------- /fctCmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/posener/complete" 9 | ) 10 | 11 | type fctCmd struct { 12 | execFunc func([]string) 13 | helpMsg string 14 | description string 15 | completion complete.Command 16 | } 17 | 18 | func (c *fctCmd) Execute(args []string) { 19 | c.execFunc(args) 20 | } 21 | -------------------------------------------------------------------------------- /get.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "flag" 11 | "fmt" 12 | "os" 13 | "strconv" 14 | 15 | "github.com/FactomProject/cli" 16 | "github.com/FactomProject/factom" 17 | "github.com/posener/complete" 18 | ) 19 | 20 | var get = func() *fctCmd { 21 | cmd := new(fctCmd) 22 | cmd.helpMsg = "factom-cli get allentries|authorities|chainhead|" + 23 | "currentminute|ablock|dblock|eblock|ecblock|fblock|entry|firstentry|" + 24 | "head|heights|pendingentries|pendingtransactions|raw|tps|walletheight" 25 | cmd.description = "Get data about Factom Chains, Entries, and Blocks" 26 | cmd.completion = complete.Command{ 27 | Sub: complete.Commands{ 28 | "allentries": getAllEntries.completion, 29 | "authorities": complete.Command{}, 30 | "chainhead": getChainHead.completion, 31 | "currentminute": getCurrentMinute.completion, 32 | "ablock": getABlock.completion, 33 | "dblock": getDBlock.completion, 34 | "eblock": getEBlock.completion, 35 | "ecblock": getECBlock.completion, 36 | "fblock": getFBlock.completion, 37 | "entry": getEntry.completion, 38 | "firstentry": getFirstEntry.completion, 39 | "head": getHead.completion, 40 | "heights": getHeights.completion, 41 | "pendingentries": getPendingEntries.completion, 42 | "pendingtransactions": getPendingTransactions.completion, 43 | "raw": getraw.completion, 44 | "tps": getTPS.completion, 45 | "walletheight": complete.Command{}, 46 | }, 47 | } 48 | cmd.execFunc = func(args []string) { 49 | os.Args = args 50 | flag.Parse() 51 | args = flag.Args() 52 | 53 | c := cli.New() 54 | c.Handle("allentries", getAllEntries) 55 | c.Handle("authorities", getAuthorities) 56 | c.Handle("chainhead", getChainHead) 57 | c.Handle("currentminute", getCurrentMinute) 58 | c.Handle("ablock", getABlock) 59 | c.Handle("dblock", getDBlock) 60 | c.Handle("eblock", getEBlock) 61 | c.Handle("ecblock", getECBlock) 62 | c.Handle("fblock", getFBlock) 63 | c.Handle("entry", getEntry) 64 | c.Handle("firstentry", getFirstEntry) 65 | c.Handle("head", getHead) 66 | c.Handle("heights", getHeights) 67 | c.Handle("pendingentries", getPendingEntries) 68 | c.Handle("pendingtransactions", getPendingTransactions) 69 | c.Handle("raw", getraw) 70 | c.Handle("tps", getTPS) 71 | c.Handle("walletheight", getWalletHeight) 72 | 73 | // Deprecated calls - be backwards compatible 74 | c.Handle("abheight", Abheight) 75 | c.Handle("dbheight", Dbheight) 76 | c.Handle("ecbheight", Ecbheight) 77 | c.Handle("fbheight", Fbheight) 78 | 79 | c.HandleDefaultFunc(func(args []string) { 80 | fmt.Println(cmd.helpMsg) 81 | }) 82 | c.Execute(args) 83 | } 84 | help.Add("get", cmd) 85 | return cmd 86 | }() 87 | 88 | var getAllEntries = func() *fctCmd { 89 | cmd := new(fctCmd) 90 | cmd.helpMsg = "factom-cli get allentries [-n NAME1 -h HEXNAME2" + 91 | " ...|CHAINID] [-E]" 92 | cmd.description = "Get all of the Entries confirmed in a Chain. -n and" + 93 | " -h to specify the chain name. -E EntryHash." 94 | cmd.completion = complete.Command{ 95 | Flags: complete.Flags{ 96 | "-n": complete.PredictAnything, 97 | "-h": complete.PredictAnything, 98 | 99 | "-E": complete.PredictNothing, 100 | }, 101 | } 102 | cmd.execFunc = func(args []string) { 103 | var ( 104 | nAcii namesASCII 105 | nHex namesHex 106 | ) 107 | os.Args = args 108 | edisp := flag.Bool("E", false, "display only the EntryHashes") 109 | nameCollector = make([][]byte, 0) 110 | flag.Var(&nAcii, "n", "ascii name component") 111 | flag.Var(&nHex, "h", "hex binary name component") 112 | flag.Parse() 113 | args = flag.Args() 114 | 115 | var chainid string 116 | 117 | if len(args) < 1 && len(nameCollector) == 0 { 118 | fmt.Println(cmd.helpMsg) 119 | return 120 | } 121 | if len(nameCollector) != 0 { 122 | chainid = nametoid(nameCollector) 123 | } else { 124 | chainid = args[0] 125 | } 126 | 127 | es, err := factom.GetAllChainEntries(chainid) 128 | if err != nil { 129 | for i, e := range es { 130 | switch { 131 | case *edisp: 132 | fmt.Printf("%x\n", e.Hash()) 133 | default: 134 | fmt.Printf("Entry [%d] {\n%s}\n", i, e) 135 | } 136 | } 137 | errorln(err) 138 | return 139 | } 140 | 141 | for i, e := range es { 142 | switch { 143 | case *edisp: 144 | fmt.Printf("%x\n", e.Hash()) 145 | default: 146 | fmt.Printf("Entry [%d] {\n%s}\n", i, e) 147 | } 148 | } 149 | } 150 | help.Add("get allentries", cmd) 151 | return cmd 152 | }() 153 | 154 | var getAuthorities = func() *fctCmd { 155 | cmd := new(fctCmd) 156 | cmd.helpMsg = "factom-cli get authorities" 157 | cmd.description = "Get information about the authority servers on the " + 158 | "Factom network" 159 | cmd.execFunc = func(args []string) { 160 | as, err := factom.GetAuthorities() 161 | if err != nil { 162 | errorln(err) 163 | return 164 | } 165 | for _, a := range as { 166 | fmt.Println(a) 167 | } 168 | } 169 | help.Add("get authorities", cmd) 170 | return cmd 171 | }() 172 | 173 | var getChainHead = func() *fctCmd { 174 | cmd := new(fctCmd) 175 | cmd.helpMsg = "factom-cli get chainhead [-n NAME1 -h HEXNAME2 ...|CHAINID] [-K]" 176 | cmd.description = "Get the latest Entry Block of the specified Chain. -n " + 177 | "and -h to specify the chain name. -K KeyMR." 178 | cmd.completion = complete.Command{ 179 | Flags: complete.Flags{ 180 | "-n": complete.PredictAnything, 181 | "-h": complete.PredictAnything, 182 | 183 | "-K": complete.PredictNothing, 184 | }, 185 | } 186 | cmd.execFunc = func(args []string) { 187 | var ( 188 | nAcii namesASCII 189 | nHex namesHex 190 | ) 191 | os.Args = args 192 | kdisp := flag.Bool("K", false, "display only the Entry Block Key Merkel Root") 193 | 194 | nameCollector = make([][]byte, 0) 195 | flag.Var(&nAcii, "n", "ascii name component") 196 | flag.Var(&nHex, "h", "hex binary name component") 197 | flag.Parse() 198 | args = flag.Args() 199 | 200 | var chainid string 201 | 202 | if len(args) < 1 && len(nameCollector) == 0 { 203 | fmt.Println(cmd.helpMsg) 204 | return 205 | } 206 | if len(nameCollector) != 0 { 207 | chainid = nametoid(nameCollector) 208 | } else { 209 | chainid = args[0] 210 | } 211 | 212 | head, inPL, err := factom.GetChainHead(chainid) 213 | if err != nil { 214 | errorln(err) 215 | return 216 | } 217 | 218 | if head == "" && inPL { 219 | errorln(factom.ErrChainPending) 220 | return 221 | } 222 | 223 | eblock, err := factom.GetEBlock(head) 224 | if err != nil { 225 | errorln(err) 226 | return 227 | } 228 | 229 | switch { 230 | case *kdisp: 231 | fmt.Println(head) 232 | default: 233 | fmt.Println("EBlock:", head) 234 | fmt.Println(eblock) 235 | } 236 | } 237 | help.Add("get chainhead", cmd) 238 | return cmd 239 | }() 240 | 241 | var getABlock = func() *fctCmd { 242 | cmd := new(fctCmd) 243 | cmd.helpMsg = "factom-cli get ablock [-RDBPL] HEIGHT|KEYMR" 244 | cmd.description = "Get an Admin Block from factom by its Key Merkel Root " + 245 | "or by its Height" 246 | cmd.completion = complete.Command{ 247 | Flags: complete.Flags{ 248 | "-R": complete.PredictNothing, 249 | "-D": complete.PredictNothing, 250 | "-B": complete.PredictNothing, 251 | "-P": complete.PredictNothing, 252 | "-L": complete.PredictNothing, 253 | }, 254 | Args: complete.PredictNothing, 255 | } 256 | cmd.execFunc = func(args []string) { 257 | os.Args = args 258 | rdisp := flag.Bool("R", false, "display the hex encoding of the raw Directory Block") 259 | ddisp := flag.Bool("D", false, "display only the Directory Block height") 260 | bdisp := flag.Bool("B", false, "display only the Backreference Hash") 261 | pdisp := flag.Bool("P", false, "display only the Previous Backreference Hash") 262 | ldisp := flag.Bool("L", false, "display only the Lookup Hash") 263 | flag.Parse() 264 | args = flag.Args() 265 | if len(args) < 1 { 266 | fmt.Println(cmd.helpMsg) 267 | return 268 | } 269 | 270 | ablock, raw, err := func() (ablock *factom.ABlock, raw []byte, err error) { 271 | // By KMR 272 | if len(args[0]) == 64 { 273 | ablock, err = factom.GetABlock(args[0]) 274 | } else { 275 | // By height 276 | i, err := strconv.ParseInt(args[0], 10, 64) 277 | if err != nil { 278 | return nil, nil, err 279 | } 280 | ablock, err = factom.GetABlockByHeight(i) 281 | } 282 | if *rdisp && err == nil { 283 | raw, err = factom.GetRaw(ablock.LookupHash) 284 | } 285 | 286 | return ablock, raw, err 287 | }() 288 | if err != nil { 289 | errorln(err) 290 | return 291 | } 292 | 293 | switch { 294 | case *rdisp: 295 | fmt.Printf("%x\n", raw) 296 | case *ddisp: 297 | fmt.Println(ablock.DBHeight) 298 | case *bdisp: 299 | fmt.Println(ablock.BackReferenceHash) 300 | case *pdisp: 301 | fmt.Println(ablock.PrevBackreferenceHash) 302 | case *ldisp: 303 | fmt.Println(ablock.LookupHash) 304 | default: 305 | fmt.Println(ablock) 306 | } 307 | } 308 | help.Add("get ablock", cmd) 309 | return cmd 310 | }() 311 | 312 | var getCurrentMinute = func() *fctCmd { 313 | cmd := new(fctCmd) 314 | cmd.helpMsg = "factom-cli get currentminute [-LDMBNTSXFR]" 315 | cmd.description = "Get information about the current minute and other properties of the factom network." 316 | cmd.completion = complete.Command{ 317 | Flags: complete.Flags{ 318 | "-L": complete.PredictNothing, 319 | "-D": complete.PredictNothing, 320 | "-M": complete.PredictNothing, 321 | "-B": complete.PredictNothing, 322 | "-N": complete.PredictNothing, 323 | "-T": complete.PredictNothing, 324 | "-S": complete.PredictNothing, 325 | "-X": complete.PredictNothing, 326 | "-F": complete.PredictNothing, 327 | "-R": complete.PredictNothing, 328 | }, 329 | } 330 | cmd.execFunc = func(args []string) { 331 | os.Args = args 332 | ldisp := flag.Bool("L", false, "display only the Leader height") 333 | ddisp := flag.Bool("D", false, "display only the Directory Block height") 334 | mdisp := flag.Bool("M", false, "display only the current minute") 335 | bdisp := flag.Bool("B", false, "display only the Block start time") 336 | ndisp := flag.Bool("N", false, "display only the minute start time") 337 | tdisp := flag.Bool("T", false, "display only the current time") 338 | sdisp := flag.Bool("S", false, "display only the Directorty Block in seconds") 339 | xdisp := flag.Bool("X", false, "display only the stall detected value") 340 | fdisp := flag.Bool("F", false, "display only the Fault timeout") 341 | rdisp := flag.Bool("R", false, "display only the round timeout") 342 | flag.Parse() 343 | args = flag.Args() 344 | 345 | info, err := factom.GetCurrentMinute() 346 | if err != nil { 347 | errorln(err) 348 | return 349 | } 350 | 351 | switch { 352 | case *ldisp: 353 | fmt.Println(info.LeaderHeight) 354 | case *ddisp: 355 | fmt.Println(info.DirectoryBlockHeight) 356 | case *mdisp: 357 | fmt.Println(info.Minute) 358 | case *bdisp: 359 | fmt.Println(info.CurrentBlockStartTime) 360 | case *ndisp: 361 | fmt.Println(info.CurrentMinuteStartTime) 362 | case *tdisp: 363 | fmt.Println(info.CurrentTime) 364 | case *sdisp: 365 | fmt.Println(info.DirectoryBlockInSeconds) 366 | case *xdisp: 367 | fmt.Println(info.StallDetected) 368 | case *fdisp: 369 | fmt.Println(info.FaultTimeout) 370 | case *rdisp: 371 | fmt.Println(info.RoundTimeout) 372 | default: 373 | fmt.Println(info) 374 | } 375 | } 376 | help.Add("get currentminute", cmd) 377 | return cmd 378 | }() 379 | 380 | var getDBlock = func() *fctCmd { 381 | cmd := new(fctCmd) 382 | cmd.helpMsg = "factom-cli get dblock [-RHKAVNBPFTDC] HEIGHT|KEYMR" 383 | cmd.description = "Get a Directory Block from factom by its Key Merkel " + 384 | "Root or by its Height" 385 | cmd.completion = complete.Command{ 386 | Flags: complete.Flags{ 387 | "-R": complete.PredictNothing, 388 | "-H": complete.PredictNothing, 389 | "-K": complete.PredictNothing, 390 | "-A": complete.PredictNothing, 391 | "-V": complete.PredictNothing, 392 | "-N": complete.PredictNothing, 393 | "-B": complete.PredictNothing, 394 | "-P": complete.PredictNothing, 395 | "-F": complete.PredictNothing, 396 | "-T": complete.PredictNothing, 397 | "-D": complete.PredictNothing, 398 | "-C": complete.PredictNothing, 399 | }, 400 | Args: complete.PredictNothing, 401 | } 402 | cmd.execFunc = func(args []string) { 403 | os.Args = args 404 | rdisp := flag.Bool("R", false, "display the hex encoding of the raw Directory Block") 405 | hdisp := flag.Bool("H", false, "display only the Directory Block Hash") 406 | kdisp := flag.Bool("K", false, "display only the Directory Block Key Merkel Root") 407 | adisp := flag.Bool("A", false, "display only the Directory Block Header Hash") 408 | vdisp := flag.Bool("V", false, "display only the Directory Block Header Version") 409 | ndisp := flag.Bool("N", false, "display only the Network ID") 410 | bdisp := flag.Bool("B", false, "display only the Directory Block Body Merkel Root") 411 | pdisp := flag.Bool("P", false, "display only the Previous Directory Block Key Merkel Root") 412 | fdisp := flag.Bool("F", false, "display only the Previous Directory Block Full Hash") 413 | tdisp := flag.Bool("T", false, "display only the Directory Block Timestamp") 414 | ddisp := flag.Bool("D", false, "display only the Directory Block Height") 415 | cdisp := flag.Bool("C", false, "display only the Directory Block Count") 416 | flag.Parse() 417 | args = flag.Args() 418 | if len(args) < 1 { 419 | fmt.Println(cmd.helpMsg) 420 | return 421 | } 422 | 423 | dblock, raw, err := func() (dblock *factom.DBlock, raw []byte, err error) { 424 | if len(args[0]) == 64 { 425 | dblock, err = factom.GetDBlock(args[0]) 426 | } else { 427 | i, err := strconv.ParseInt(args[0], 10, 64) 428 | if err != nil { 429 | return nil, nil, err 430 | } 431 | dblock, err = factom.GetDBlockByHeight(i) 432 | } 433 | if *rdisp && err == nil { 434 | raw, err = factom.GetRaw(dblock.KeyMR) 435 | } 436 | 437 | return dblock, raw, err 438 | }() 439 | if err != nil { 440 | errorln(err) 441 | return 442 | } 443 | 444 | switch { 445 | case *rdisp: 446 | fmt.Printf("%x\n", raw) 447 | case *hdisp: 448 | fmt.Println(dblock.DBHash) 449 | case *kdisp: 450 | fmt.Println(dblock.KeyMR) 451 | case *adisp: 452 | fmt.Println(dblock.HeaderHash) 453 | case *vdisp: 454 | fmt.Println(dblock.Header.Version) 455 | case *ndisp: 456 | fmt.Println(dblock.Header.NetworkID) 457 | case *bdisp: 458 | fmt.Println(dblock.Header.BodyMR) 459 | case *pdisp: 460 | fmt.Println(dblock.Header.PrevKeyMR) 461 | case *fdisp: 462 | fmt.Println(dblock.Header.PrevFullHash) 463 | case *tdisp: 464 | fmt.Println(dblock.Header.Timestamp) 465 | case *ddisp: 466 | fmt.Println(dblock.Header.DBHeight) 467 | case *cdisp: 468 | fmt.Println(dblock.Header.BlockCount) 469 | default: 470 | fmt.Println(dblock) 471 | } 472 | } 473 | help.Add("get dblock", cmd) 474 | return cmd 475 | }() 476 | 477 | var getEBlock = func() *fctCmd { 478 | cmd := new(fctCmd) 479 | cmd.helpMsg = "factom-cli get eblock KEYMR" 480 | cmd.description = "Get Entry Block by Key Merkle Root" 481 | cmd.completion = complete.Command{ 482 | Args: complete.PredictNothing, 483 | } 484 | cmd.execFunc = func(args []string) { 485 | os.Args = args 486 | flag.Parse() 487 | args = flag.Args() 488 | if len(args) < 1 { 489 | fmt.Println(cmd.helpMsg) 490 | return 491 | } 492 | 493 | keymr := args[0] 494 | eblock, err := factom.GetEBlock(keymr) 495 | if err != nil { 496 | errorln(err) 497 | return 498 | } 499 | fmt.Println(eblock) 500 | } 501 | help.Add("get eblock", cmd) 502 | return cmd 503 | }() 504 | 505 | var getECBlock = func() *fctCmd { 506 | cmd := new(fctCmd) 507 | cmd.helpMsg = "factom-cli get ecblock [-RBPLDAHF] HEIGHT|KEYMR" 508 | cmd.description = "Get an Entry Credit Block by Key Merkle Root or by " + 509 | "height" 510 | cmd.completion = complete.Command{ 511 | Flags: complete.Flags{ 512 | "-R": complete.PredictNothing, 513 | "-B": complete.PredictNothing, 514 | "-P": complete.PredictNothing, 515 | "-L": complete.PredictNothing, 516 | "-D": complete.PredictNothing, 517 | "-A": complete.PredictNothing, 518 | "-H": complete.PredictNothing, 519 | "-F": complete.PredictNothing, 520 | }, 521 | } 522 | cmd.execFunc = func(args []string) { 523 | os.Args = args 524 | rdisp := flag.Bool("R", false, "display the hex encoding of the raw Entry Credit Block") 525 | bdisp := flag.Bool("B", false, "display only the Body Hash") 526 | pdisp := flag.Bool("P", false, "display only the Previous Header Hash") 527 | ldisp := flag.Bool("L", false, "display only the Previous Full Hash") 528 | ddisp := flag.Bool("D", false, "display only the Directory Block Height") 529 | adisp := flag.Bool("A", false, "display only the Head Expansion Area") 530 | hdisp := flag.Bool("H", false, "display only the Header Hash") 531 | fdisp := flag.Bool("F", false, "display only the Full Hash") 532 | flag.Parse() 533 | args = flag.Args() 534 | if len(args) < 1 { 535 | fmt.Println(cmd.helpMsg) 536 | return 537 | } 538 | 539 | ecblock, raw, err := func() (ecblock *factom.ECBlock, raw []byte, err error) { 540 | if len(args[0]) == 64 { 541 | ecblock, err = factom.GetECBlock(args[0]) 542 | } else { 543 | i, err := strconv.ParseInt(args[0], 10, 64) 544 | if err != nil { 545 | return nil, nil, err 546 | } 547 | ecblock, err = factom.GetECBlockByHeight(i) 548 | } 549 | if *rdisp && err == nil { 550 | raw, err = factom.GetRaw(ecblock.FullHash) 551 | } 552 | 553 | return ecblock, raw, err 554 | }() 555 | if err != nil { 556 | errorln(err) 557 | return 558 | } 559 | 560 | switch { 561 | case *rdisp: 562 | fmt.Printf("%x\n", raw) 563 | case *bdisp: 564 | fmt.Println(ecblock.Header.BodyHash) 565 | case *pdisp: 566 | fmt.Println(ecblock.Header.PrevHeaderHash) 567 | case *ldisp: 568 | fmt.Println(ecblock.Header.PrevFullHash) 569 | case *ddisp: 570 | fmt.Println(ecblock.Header.DBHeight) 571 | case *adisp: 572 | fmt.Println(ecblock.Header.HeaderExpansionArea) 573 | case *hdisp: 574 | fmt.Println(ecblock.HeaderHash) 575 | case *fdisp: 576 | fmt.Println(ecblock.FullHash) 577 | default: 578 | fmt.Println(ecblock) 579 | } 580 | } 581 | help.Add("get ecblock", cmd) 582 | return cmd 583 | }() 584 | 585 | var getFBlock = func() *fctCmd { 586 | cmd := new(fctCmd) 587 | cmd.helpMsg = "factom-cli get fblock [-RBPLED] KEYMR|HEIGHT" 588 | cmd.description = "Get a Factoid Block by its Key Merkle Root or Height" 589 | cmd.completion = complete.Command{ 590 | Flags: complete.Flags{ 591 | "-R": complete.PredictNothing, 592 | "-B": complete.PredictNothing, 593 | "-P": complete.PredictNothing, 594 | "-L": complete.PredictNothing, 595 | "-E": complete.PredictNothing, 596 | "-D": complete.PredictNothing, 597 | }, 598 | Args: complete.PredictNothing, 599 | } 600 | cmd.execFunc = func(args []string) { 601 | os.Args = args 602 | rdisp := flag.Bool("R", false, "display the hex encoding of the raw Factoid Block") 603 | bdisp := flag.Bool("B", false, "display only the Body Merkel Root") 604 | pdisp := flag.Bool("P", false, "display only the Previous Key Merkel Root") 605 | ldisp := flag.Bool("L", false, "display only the Previous Ledger Key Merkel Root") 606 | edisp := flag.Bool("E", false, "display only the Exchange Rate") 607 | ddisp := flag.Bool("D", false, "display only the Directory Block Height") 608 | flag.Parse() 609 | args = flag.Args() 610 | if len(args) < 1 { 611 | fmt.Println(cmd.helpMsg) 612 | return 613 | } 614 | 615 | fblock, raw, err := func() (fblock *factom.FBlock, raw []byte, err error) { 616 | if len(args[0]) == 64 { 617 | fblock, err = factom.GetFBlock(args[0]) 618 | } else { 619 | i, err := strconv.ParseInt(args[0], 10, 64) 620 | if err != nil { 621 | return nil, nil, err 622 | } 623 | fblock, err = factom.GetFBlockByHeight(i) 624 | } 625 | if *rdisp && err == nil { 626 | raw, err = factom.GetRaw(fblock.KeyMR) 627 | } 628 | 629 | return fblock, raw, err 630 | }() 631 | if err != nil { 632 | errorln(err) 633 | return 634 | } 635 | 636 | switch { 637 | case *rdisp: 638 | fmt.Printf("%x\n", raw) 639 | case *bdisp: 640 | fmt.Println(fblock.BodyMR) 641 | case *pdisp: 642 | fmt.Println(fblock.PrevKeyMR) 643 | case *ldisp: 644 | fmt.Println(fblock.PrevLedgerKeyMR) 645 | case *edisp: 646 | fmt.Println(fblock.ExchRate) 647 | case *ddisp: 648 | fmt.Println(fblock.DBHeight) 649 | default: 650 | data, err := json.Marshal(fblock) 651 | if err != nil { 652 | errorln(err) 653 | return 654 | } 655 | var out bytes.Buffer 656 | json.Indent(&out, data, "", "\t") 657 | fmt.Printf("%s\n", out.Bytes()) 658 | } 659 | } 660 | help.Add("get fblock", cmd) 661 | return cmd 662 | }() 663 | 664 | var getEntry = func() *fctCmd { 665 | cmd := new(fctCmd) 666 | cmd.helpMsg = "factom-cli get entry [-RC] HASH" 667 | cmd.description = "Get Entry by Hash. -R raw entry. -C ChainID" 668 | cmd.completion = complete.Command{ 669 | Flags: complete.Flags{ 670 | "-R": complete.PredictNothing, 671 | "-C": complete.PredictNothing, 672 | }, 673 | Args: complete.PredictNothing, 674 | } 675 | cmd.execFunc = func(args []string) { 676 | os.Args = args 677 | rdisp := flag.Bool("R", false, "display the hex encoding of the raw Entry") 678 | cdisp := flag.Bool("C", false, "display only the Chain ID") 679 | flag.Parse() 680 | args = flag.Args() 681 | if len(args) < 1 { 682 | fmt.Println(cmd.helpMsg) 683 | return 684 | } 685 | 686 | hash := args[0] 687 | entry, err := factom.GetEntry(hash) 688 | if err != nil { 689 | errorln(err) 690 | return 691 | } 692 | switch { 693 | case *rdisp: 694 | b, err := entry.MarshalBinary() 695 | if err != nil { 696 | errorln(err) 697 | return 698 | } 699 | fmt.Printf("%x\n", b) 700 | case *cdisp: 701 | fmt.Println(entry.ChainID) 702 | default: 703 | fmt.Println(entry) 704 | } 705 | } 706 | help.Add("get entry", cmd) 707 | return cmd 708 | }() 709 | 710 | var getFirstEntry = func() *fctCmd { 711 | cmd := new(fctCmd) 712 | cmd.helpMsg = "factom-cli get firstentry [-n NAME1 -h HEXNAME2 ...|CHAINID] [-REC]" 713 | cmd.description = "Get the first Entry in a Chain. -R RawEntry. -E EntryHash. -C ChainID." 714 | cmd.completion = complete.Command{ 715 | Flags: complete.Flags{ 716 | "-n": complete.PredictAnything, 717 | "-h": complete.PredictAnything, 718 | 719 | "-R": complete.PredictNothing, 720 | "-E": complete.PredictNothing, 721 | "-C": complete.PredictNothing, 722 | }, 723 | Args: complete.PredictNothing, 724 | } 725 | cmd.execFunc = func(args []string) { 726 | var ( 727 | nAcii namesASCII 728 | nHex namesHex 729 | ) 730 | os.Args = args 731 | rdisp := flag.Bool("R", false, "display the hex encoding of the raw Entry") 732 | edisp := flag.Bool("E", false, "display only the EntryHash") 733 | cdisp := flag.Bool("C", false, "display only the ChainID") 734 | 735 | nameCollector = make([][]byte, 0) 736 | flag.Var(&nAcii, "n", "ascii name component") 737 | flag.Var(&nHex, "h", "hex binary name component") 738 | flag.Parse() 739 | args = flag.Args() 740 | 741 | var chainid string 742 | 743 | if len(args) < 1 && len(nameCollector) == 0 { 744 | fmt.Println(cmd.helpMsg) 745 | return 746 | } 747 | if len(nameCollector) != 0 { 748 | chainid = nametoid(nameCollector) 749 | } else { 750 | chainid = args[0] 751 | } 752 | 753 | entry, err := factom.GetFirstEntry(chainid) 754 | if err != nil { 755 | errorln(err) 756 | return 757 | } 758 | 759 | switch { 760 | case *rdisp: 761 | b, err := entry.MarshalBinary() 762 | if err != nil { 763 | errorln(err) 764 | return 765 | } 766 | fmt.Printf("%x\n", b) 767 | case *edisp: 768 | fmt.Printf("%x\n", entry.Hash()) 769 | case *cdisp: 770 | fmt.Println(entry.ChainID) 771 | default: 772 | fmt.Println(entry) 773 | } 774 | } 775 | help.Add("get firstentry", cmd) 776 | return cmd 777 | }() 778 | 779 | var getHead = func() *fctCmd { 780 | cmd := new(fctCmd) 781 | cmd.helpMsg = "factom-cli get head [-RHKAVNBPFTDC]" 782 | cmd.description = "Get the latest completed Directory Block" 783 | cmd.completion = complete.Command{ 784 | Flags: complete.Flags{ 785 | "-R": complete.PredictNothing, 786 | "-H": complete.PredictNothing, 787 | "-K": complete.PredictNothing, 788 | "-A": complete.PredictNothing, 789 | "-V": complete.PredictNothing, 790 | "-N": complete.PredictNothing, 791 | "-B": complete.PredictNothing, 792 | "-P": complete.PredictNothing, 793 | "-F": complete.PredictNothing, 794 | "-T": complete.PredictNothing, 795 | "-D": complete.PredictNothing, 796 | "-C": complete.PredictNothing, 797 | }, 798 | } 799 | cmd.execFunc = func(args []string) { 800 | os.Args = args 801 | rdisp := flag.Bool("R", false, "display the hex encoding of the raw Directory Block") 802 | hdisp := flag.Bool("H", false, "display only the Directory Block Hash") 803 | kdisp := flag.Bool("K", false, "display only the Directory Block Key Merkel Root") 804 | adisp := flag.Bool("A", false, "display only the Directory Block Header Hash") 805 | vdisp := flag.Bool("V", false, "display only the Directory Block Header Version") 806 | ndisp := flag.Bool("N", false, "display only the Network ID") 807 | bdisp := flag.Bool("B", false, "display only the Directory Block Body Merkel Root") 808 | pdisp := flag.Bool("P", false, "display only the Previous Directory Block Key Merkel Root") 809 | fdisp := flag.Bool("F", false, "display only the Previous Directory Block Full Hash") 810 | tdisp := flag.Bool("T", false, "display only the Directory Block Timestamp") 811 | ddisp := flag.Bool("D", false, "display only the Directory Block Height") 812 | cdisp := flag.Bool("C", false, "display only the Directory Block Count") 813 | flag.Parse() 814 | args = flag.Args() 815 | 816 | head, err := factom.GetDBlockHead() 817 | if err != nil { 818 | errorln(err) 819 | return 820 | } 821 | dblock, err := factom.GetDBlock(head) 822 | if err != nil { 823 | errorln(err) 824 | return 825 | } 826 | 827 | switch { 828 | case *rdisp: 829 | raw, err := factom.GetRaw(head) 830 | if err != nil { 831 | errorln(err) 832 | return 833 | } 834 | fmt.Printf("%x\n", raw) 835 | case *hdisp: 836 | fmt.Println(dblock.DBHash) 837 | case *kdisp: 838 | fmt.Println(dblock.KeyMR) 839 | case *adisp: 840 | fmt.Println(dblock.HeaderHash) 841 | case *vdisp: 842 | fmt.Println(dblock.Header.Version) 843 | case *ndisp: 844 | fmt.Println(dblock.Header.NetworkID) 845 | case *bdisp: 846 | fmt.Println(dblock.Header.BodyMR) 847 | case *pdisp: 848 | fmt.Println(dblock.Header.PrevKeyMR) 849 | case *fdisp: 850 | fmt.Println(dblock.Header.PrevFullHash) 851 | case *tdisp: 852 | fmt.Println(dblock.Header.Timestamp) 853 | case *ddisp: 854 | fmt.Println(dblock.Header.DBHeight) 855 | case *cdisp: 856 | fmt.Println(dblock.Header.BlockCount) 857 | default: 858 | fmt.Println(dblock) 859 | } 860 | } 861 | help.Add("get head", cmd) 862 | return cmd 863 | }() 864 | 865 | var getHeights = func() *fctCmd { 866 | cmd := new(fctCmd) 867 | cmd.helpMsg = "factom-cli get heights [-DLBE]" 868 | cmd.description = "Get the current heights of various items in factomd." 869 | cmd.completion = complete.Command{ 870 | Flags: complete.Flags{ 871 | "-D": complete.PredictNothing, 872 | "-L": complete.PredictNothing, 873 | "-B": complete.PredictNothing, 874 | "-E": complete.PredictNothing, 875 | }, 876 | } 877 | cmd.execFunc = func(args []string) { 878 | os.Args = args 879 | ddisp := flag.Bool("D", false, "display only the Directory Block height") 880 | ldisp := flag.Bool("L", false, "display only the Leader height") 881 | bdisp := flag.Bool("B", false, "display only the EntryBlock height") 882 | edisp := flag.Bool("E", false, "display only the Entry height") 883 | flag.Parse() 884 | args = flag.Args() 885 | 886 | heights, err := factom.GetHeights() 887 | if err != nil { 888 | errorln(err) 889 | return 890 | } 891 | 892 | switch { 893 | case *ddisp: 894 | fmt.Println(heights.DirectoryBlockHeight) 895 | case *ldisp: 896 | fmt.Println(heights.LeaderHeight) 897 | case *bdisp: 898 | fmt.Println(heights.EntryBlockHeight) 899 | case *edisp: 900 | fmt.Println(heights.EntryHeight) 901 | default: 902 | fmt.Print(heights.String()) 903 | } 904 | } 905 | help.Add("get heights", cmd) 906 | return cmd 907 | }() 908 | 909 | var getWalletHeight = func() *fctCmd { 910 | cmd := new(fctCmd) 911 | cmd.helpMsg = "factom-cli get walletheight" 912 | cmd.description = "Get the number of factoid blocks factom-walletd has cached" 913 | cmd.execFunc = func(args []string) { 914 | height, err := factom.GetWalletHeight() 915 | if err != nil { 916 | errorln(err) 917 | return 918 | } 919 | fmt.Printf("WalletHeight: %v\n", height) 920 | } 921 | help.Add("get walletheight", cmd) 922 | return cmd 923 | }() 924 | 925 | var properties = func() *fctCmd { 926 | cmd := new(fctCmd) 927 | cmd.helpMsg = "factom-cli properties [-CFAWL]" 928 | cmd.description = "Get version information about factomd and the factom wallet" 929 | cmd.completion = complete.Command{ 930 | Flags: complete.Flags{ 931 | "-C": complete.PredictNothing, 932 | "-F": complete.PredictNothing, 933 | "-A": complete.PredictNothing, 934 | "-W": complete.PredictNothing, 935 | "-L": complete.PredictNothing, 936 | }, 937 | } 938 | cmd.execFunc = func(args []string) { 939 | os.Args = args 940 | cdisp := flag.Bool("C", false, "display only the CLI version") 941 | fdisp := flag.Bool("F", false, "display only the factomd version") 942 | adisp := flag.Bool("A", false, "display only the factomd API version") 943 | wdisp := flag.Bool("W", false, "display only the factom-wallet version") 944 | ldisp := flag.Bool("L", false, "display only the wallet API version") 945 | flag.Parse() 946 | args = flag.Args() 947 | 948 | props, err := factom.GetProperties() 949 | if err != nil { 950 | errorln(err) 951 | return 952 | } 953 | 954 | switch { 955 | case *cdisp: 956 | fmt.Println(FactomcliVersion) 957 | case *fdisp: 958 | fmt.Println(props.FactomdVersion) 959 | case *adisp: 960 | fmt.Println(props.FactomdAPIVersion) 961 | case *wdisp: 962 | fmt.Println(props.WalletVersion) 963 | case *ldisp: 964 | fmt.Println(props.WalletAPIVersion) 965 | default: 966 | fmt.Println("CLI Version:", FactomcliVersion) 967 | fmt.Println(props) 968 | } 969 | } 970 | help.Add("properties", cmd) 971 | return cmd 972 | }() 973 | 974 | var getPendingEntries = func() *fctCmd { 975 | cmd := new(fctCmd) 976 | cmd.helpMsg = "factom-cli get pendingentries [-E]" 977 | cmd.description = "Get all pending entries, which may not yet be written" + 978 | " to blockchain. -E EntryHash." 979 | cmd.completion = complete.Command{ 980 | Flags: complete.Flags{ 981 | "-E": complete.PredictNothing, 982 | }, 983 | } 984 | cmd.execFunc = func(args []string) { 985 | os.Args = args 986 | edisp := flag.Bool( 987 | "E", 988 | false, 989 | "display only the Entry Hashes", 990 | ) 991 | flag.Parse() 992 | args = flag.Args() 993 | 994 | entries, err := factom.GetPendingEntries() 995 | if err != nil { 996 | errorln(err) 997 | return 998 | } 999 | 1000 | for _, ents := range entries { 1001 | switch { 1002 | case *edisp: 1003 | fmt.Println(ents.EntryHash) 1004 | default: 1005 | fmt.Println("ChainID:", ents.ChainID) 1006 | fmt.Println("Entryhash:", ents.EntryHash) 1007 | fmt.Println("") 1008 | } 1009 | } 1010 | } 1011 | help.Add("get pendingentries", cmd) 1012 | return cmd 1013 | }() 1014 | 1015 | var getPendingTransactions = func() *fctCmd { 1016 | cmd := new(fctCmd) 1017 | cmd.helpMsg = "factom-cli get pendingtransactions [-T]" 1018 | cmd.description = "Get all pending factoid transacitons, which may not yet be written to blockchain. -T TxID." 1019 | cmd.completion = complete.Command{ 1020 | Flags: complete.Flags{ 1021 | "-T": complete.PredictNothing, 1022 | }, 1023 | } 1024 | cmd.execFunc = func(args []string) { 1025 | os.Args = args 1026 | tdisp := flag.Bool( 1027 | "T", 1028 | false, 1029 | "display only the Transaction IDs", 1030 | ) 1031 | flag.Parse() 1032 | args = flag.Args() 1033 | 1034 | trans, err := factom.GetPendingTransactions() 1035 | if err != nil { 1036 | errorln(err) 1037 | return 1038 | } 1039 | 1040 | for _, tran := range trans { 1041 | if len(tran.Inputs) != 0 { 1042 | switch { 1043 | case *tdisp: 1044 | fmt.Println(tran.TxID) 1045 | default: 1046 | fmt.Println("TxID:", tran.TxID) 1047 | for _, in := range tran.Inputs { 1048 | fmt.Println("Input:", in.Address, in.Amount/1e9) 1049 | } 1050 | 1051 | if len(tran.Outputs) != 0 { 1052 | 1053 | for _, out := range tran.Outputs { 1054 | fmt.Println("Output:", out.Address, out.Amount/1e9) 1055 | } 1056 | } 1057 | 1058 | if len(tran.ECOutputs) != 0 { 1059 | for _, ecout := range tran.ECOutputs { 1060 | fmt.Println("ECOutput:", ecout.Address, ecout.Amount/1e9) 1061 | } 1062 | } 1063 | if tran.Fees != 0 { 1064 | fmt.Printf("Fees: %8.8f", float64(tran.Fees)/1e9) 1065 | } 1066 | fmt.Println("") 1067 | } 1068 | } 1069 | } 1070 | 1071 | } 1072 | help.Add("get pendingtransactions", cmd) 1073 | return cmd 1074 | }() 1075 | 1076 | var getTPS = func() *fctCmd { 1077 | cmd := new(fctCmd) 1078 | cmd.helpMsg = "factom-cli get tps [-IT]" 1079 | cmd.description = "Get the current instant and total average rate of " + 1080 | "Transactions Per Second." 1081 | cmd.completion = complete.Command{ 1082 | Flags: complete.Flags{ 1083 | "-I": complete.PredictNothing, 1084 | "-T": complete.PredictNothing, 1085 | }, 1086 | } 1087 | cmd.execFunc = func(args []string) { 1088 | os.Args = args 1089 | idisp := flag.Bool("I", false, "display only the instant TPS rate") 1090 | tdisp := flag.Bool("T", false, "display only the total averaged TPS rate") 1091 | flag.Parse() 1092 | 1093 | i, t, err := factom.GetTPS() 1094 | if err != nil { 1095 | errorln(err) 1096 | return 1097 | } 1098 | 1099 | switch { 1100 | case *idisp: 1101 | fmt.Println(i) 1102 | case *tdisp: 1103 | fmt.Println(t) 1104 | default: 1105 | fmt.Printf("Instant: %0.2f\n", i) 1106 | fmt.Printf("Total: %0.2f\n", t) 1107 | } 1108 | } 1109 | help.Add("get tps", cmd) 1110 | return cmd 1111 | }() 1112 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/FactomProject/factom-cli/v2 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/FactomProject/btcutil v0.0.0-20200312214114-5fd3eaf71bd2 7 | github.com/FactomProject/cli v0.0.0-20160426220638-45b367ff72df 8 | github.com/FactomProject/factom v0.4.0 9 | github.com/FactomProject/factomd v0.0.0-20201125033516-59a959e6c40f 10 | github.com/michaelbeam/cli v0.0.0-20151028184746-e3284e1e16c4 // indirect 11 | github.com/posener/complete v1.2.3 12 | github.com/sirupsen/logrus v1.7.0 // indirect 13 | golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/FactomProject/FactomCode v0.3.6-0.20171228170625-d7e03150a9d5 h1:gLwDxcHyGx+NMW0e5+v6Di9fS3tCvZFGPExmS03cb1U= 4 | github.com/FactomProject/FactomCode v0.3.6-0.20171228170625-d7e03150a9d5/go.mod h1:7XksVta7THNbD031Ax0/dS5RKMS+ug+d7/Lmti8jAhE= 5 | github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= 6 | github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= 7 | github.com/FactomProject/bolt v1.1.1-0.20170202195217-952a1b4e9a55 h1:CndApA2wCO5lQFv9+EX0ywmHCfLDgkDSSITF9+fEMtY= 8 | github.com/FactomProject/bolt v1.1.1-0.20170202195217-952a1b4e9a55/go.mod h1:k4IUvMzQDQ2NeL83bQxOOrSJWCqkaG6ZEYxVgA/Mx2k= 9 | github.com/FactomProject/btcd v0.3.6-0.20200312193844-fe42c67cdb3a h1:dhaFYHhpWvLLNfC5QGMNvRm4gq6c/6led36PVhJihNM= 10 | github.com/FactomProject/btcd v0.3.6-0.20200312193844-fe42c67cdb3a/go.mod h1:7PRdOJFyYtTCxSWgXY121gPAvThhmHXlkGbBvR0bFpA= 11 | github.com/FactomProject/btclog v0.0.0-20150207045733-3585005305be/go.mod h1:72+jAEsDXa87FJ5CIZEH8MA9QRqek7EilxTHNqk2OaQ= 12 | github.com/FactomProject/btcutil v0.0.0-20160826074221-43986820ccd5 h1:cysv7OcQYt+P7IH1ZQAE1akzlwm+EXvxG6/0jYlJIE4= 13 | github.com/FactomProject/btcutil v0.0.0-20160826074221-43986820ccd5/go.mod h1:vneMtVKjkLz6PtiXS/HB0UJXAF77wndeJwZiqaVrN84= 14 | github.com/FactomProject/btcutil v0.0.0-20200312214114-5fd3eaf71bd2 h1:ElUUX3OIziGeRsxYGPgot4r5RM6T3tAtH3+rFfvZzII= 15 | github.com/FactomProject/btcutil v0.0.0-20200312214114-5fd3eaf71bd2/go.mod h1:lhlVaJmYuWpMYZib0G0sEOqt6ApQTQDeTiGfSDAgfTk= 16 | github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= 17 | github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= 18 | github.com/FactomProject/cli v0.0.0-20160426220638-45b367ff72df h1:byXDW+5pOzbTXszQJ6bpGOr1Ablno9m5Mff5QPb1SGk= 19 | github.com/FactomProject/cli v0.0.0-20160426220638-45b367ff72df/go.mod h1:XJwoJOABBpIPmOF5gsDDoVQQY175qrjtrEunzgTqEsE= 20 | github.com/FactomProject/dynrsrc v0.3.1 h1:4s5iFYx0dgYdiiTtdS68D2t7WUOHDX5Z8z2ctGur3VA= 21 | github.com/FactomProject/dynrsrc v0.3.1/go.mod h1:JjmEhtv+5iLNVVAN0SUO+6e0kvuuvZ4ZW6V43xmgnHk= 22 | github.com/FactomProject/ed25519 v0.0.0-20150814230546-38002c4fe7b6 h1:e2tpL8IfImGBR8qODlnb/hnwPhS4wWzoOUKoFrA3RFQ= 23 | github.com/FactomProject/ed25519 v0.0.0-20150814230546-38002c4fe7b6/go.mod h1:x54uxtF9AnHAvx2kRoIxKYLuUV+bRjUQ6u/3FhfmO5g= 24 | github.com/FactomProject/factoid v0.3.4 h1:uC0161wd2IHYFBTduUQvW4ivHLj+De12c/QsVfuZMcU= 25 | github.com/FactomProject/factoid v0.3.4/go.mod h1:ExOuHMC672nR0h9b+jaTiW9ilQeWQOap4OXixzW359M= 26 | github.com/FactomProject/factom v0.3.6-0.20190712163801-e7717c4ab072 h1:ZqIDCBU/hWkmyFtLj3vXQrVp8IjExnmw4k7P0tqDgoQ= 27 | github.com/FactomProject/factom v0.3.6-0.20190712163801-e7717c4ab072/go.mod h1:V1V8mWqOI72JBpGj/aPQZEmhDepdykxyhVRH5GxbP4E= 28 | github.com/FactomProject/factom v0.4.0 h1:FL0mHxeR440pW67Q7s8dKxEvcqxjbdi76yEFEGgwjSI= 29 | github.com/FactomProject/factom v0.4.0/go.mod h1:hmBf51LRW1PSz4qubekD+Fp4XxMJk1QuYwmhMRkPGgs= 30 | github.com/FactomProject/factomd v0.0.0-20201125033516-59a959e6c40f h1:CmL4uEpJe5rCq1RF7T9tFzH8x+mfuyhTGDZY/8hMzTQ= 31 | github.com/FactomProject/factomd v0.0.0-20201125033516-59a959e6c40f/go.mod h1:EdJsU3lDdix53CkithJfxtdlAG7IQGM5PQsMPdF5VzA= 32 | github.com/FactomProject/fastsha256 v0.2.1 h1:ICfr2Lw5Ucy785GfJSjLLG9X1mZA6dsy/xgARlZ+brs= 33 | github.com/FactomProject/fastsha256 v0.2.1/go.mod h1:/nQG+MXzxlaSxnkToKarNFTq1k0tbtSx5R82M00kC4k= 34 | github.com/FactomProject/fsnotify v0.9.0 h1:D7P7JyVP/CHGI9dPkTdKcgSoyk8uayHq/5rZYr+t+l4= 35 | github.com/FactomProject/fsnotify v0.9.0/go.mod h1:SzEMGQAVw7mK7WOh0Sx7RFa+wrsJX1jxefmoRRVRpSA= 36 | github.com/FactomProject/go-bip32 v0.3.5 h1:etsJ4Y/wA7spZl/WG830ogTHQivGcV/8sCrrmkQICNQ= 37 | github.com/FactomProject/go-bip32 v0.3.5/go.mod h1:efm/M7J/CGmQ5dPtGM0GWod5LuyShuFET6oY13168w4= 38 | github.com/FactomProject/go-bip32 v0.3.6-0.20161206200006-3b593af1c415 h1:GD0N6H8kj6B8Yw8J4x0An3feCt3dLfyvvRO6srSqcns= 39 | github.com/FactomProject/go-bip32 v0.3.6-0.20161206200006-3b593af1c415/go.mod h1:efm/M7J/CGmQ5dPtGM0GWod5LuyShuFET6oY13168w4= 40 | github.com/FactomProject/go-bip39 v0.3.5 h1:l9g92TeqCkC5NZhm72igTpf5yaYDp3Sy4CvnPYknp6U= 41 | github.com/FactomProject/go-bip39 v0.3.5/go.mod h1:ygPVOtW424QxnJMze9XYDeh4wT19V3iVDOqVUl/USkE= 42 | github.com/FactomProject/go-bip39 v0.3.6-0.20161217174232-d1007fb78d9a h1:ryYc+7pEQClwybSzPywP9+Mju7JmBbfrkzfrjJY/r2E= 43 | github.com/FactomProject/go-bip39 v0.3.6-0.20161217174232-d1007fb78d9a/go.mod h1:ygPVOtW424QxnJMze9XYDeh4wT19V3iVDOqVUl/USkE= 44 | github.com/FactomProject/go-bip44 v0.0.0-20190306062959-b541a96d8da9 h1:Wprj9FTxqhjgl7e8ZHScGwF5Wc2WrHTbEDoQKciBdhw= 45 | github.com/FactomProject/go-bip44 v0.0.0-20190306062959-b541a96d8da9/go.mod h1:4H/Y1yLgSJvRV4iqilsVhYlqXkPgf1QonpKyt6sdP+Q= 46 | github.com/FactomProject/go-flags v0.0.0-20150207050142-3116130f5d15/go.mod h1:eW8ds07YaHYZYmyuz/tY9rMEyRywQZ+hNIDb7B8AuKw= 47 | github.com/FactomProject/go-simplejson v0.5.0 h1:gO5Z2gV3+kqWcu8/TrjAbim0yFqUcFll7HzAhdxah7s= 48 | github.com/FactomProject/go-simplejson v0.5.0/go.mod h1:P6XXHV5dI0hIZte+dQ4G8tzNIiyuF/UMVuInbQd036g= 49 | github.com/FactomProject/go-socks v0.0.0-20150116071722-a7a73aaa0058/go.mod h1:WQRz0bEO3mOgLKgxpm6++CbXXGdKAE+w37bi72X3bz0= 50 | github.com/FactomProject/go-spew v0.0.0-20160301052117-ddfaec9b42f5 h1:i9apIhX38D5Wc8qdGJkFJu820El/ivkLKJQVKZdvf1M= 51 | github.com/FactomProject/go-spew v0.0.0-20160301052117-ddfaec9b42f5/go.mod h1:GITz+lAXWwMHRf2FjgacVJ9cTGsorym6PCzoUw0DiAM= 52 | github.com/FactomProject/gocoding v0.0.0-20150814232539-59666ce39524 h1:394GwlJFa1fAUgjLVMH9RUMS+Rrczpjb/AE1NWxb1Iw= 53 | github.com/FactomProject/gocoding v0.0.0-20150814232539-59666ce39524/go.mod h1:AtAVT8Uf+wlS9h/RK79oum1xym2r9UR5nb67zVEkq6Y= 54 | github.com/FactomProject/goleveldb v0.2.2-0.20170418171130-e7800c6976c5 h1:GIdre1zKgtyuc3VqjfyHZQNBj3yMO6HgWAvpO190LKg= 55 | github.com/FactomProject/goleveldb v0.2.2-0.20170418171130-e7800c6976c5/go.mod h1:35UcJZvsGJAgxqegkfDb5S78gTlx/Pypzy4rZfM1pQA= 56 | github.com/FactomProject/logrustash v0.0.0-20171005151533-9c7278ede46e h1:D/yNSYLSqUwukUw3115uW4RR+r0ftMUJFlcTC2e79pI= 57 | github.com/FactomProject/logrustash v0.0.0-20171005151533-9c7278ede46e/go.mod h1:ah6QOMU70BGkGZRLG1Irc7f+j6eDU11tzHCI+rdLcoA= 58 | github.com/FactomProject/netki-go-partner-client v0.0.0-20160324224126-426acb535e66 h1:H4nFZnW3EpbcefxbJAJxWCl7niO07QpKt8KeHtLwHoc= 59 | github.com/FactomProject/netki-go-partner-client v0.0.0-20160324224126-426acb535e66/go.mod h1:L+Od0CCfb8IzUbPM4UxLaERB1I6Bw/lD2UtzRM90pIw= 60 | github.com/FactomProject/seelog v0.0.0-20150116041118-313961b101eb/go.mod h1:8Ul2RXYkVGWGdpZvceVI8ZbYaGopswt10iWMuwbb9s8= 61 | github.com/FactomProject/serveridentity v0.0.0-20180611231115-cf42d2aa8deb h1:qU4hdSJnIMB9y+0GfHcE80v6edheTFR3VfrpuGgExc4= 62 | github.com/FactomProject/serveridentity v0.0.0-20180611231115-cf42d2aa8deb/go.mod h1:qPNpznGlx4PdRmEL7I25U/zQHrbMK1h0Az8FYahuYdo= 63 | github.com/FactomProject/snappy-go v0.0.0-20170202213131-f2f83b22c29e h1:qXLcZHL/avngRrZVkn4RCkn6L3l2Wabb5bsLSW1P3E4= 64 | github.com/FactomProject/snappy-go v0.0.0-20170202213131-f2f83b22c29e/go.mod h1:BQArcOnbPAS9l2B9Eo1R2i73/Es6PHRP0RrctmiH5jk= 65 | github.com/FactomProject/web v0.1.1-0.20200312214504-cff1e06a4e47/go.mod h1:p7dwXIht8oBpkon6PPK3u4/yNAZXomxWshTyHm2UBxw= 66 | github.com/FactomProject/websocket v0.2.1/go.mod h1:bUIwxTysrP5RW7paQDJMBhQY5g8Uj5I7yeyupo4ktag= 67 | github.com/FactomProject/winsvc v0.0.0-20150424023546-c5dc8cb850bc/go.mod h1:uAngpUPH3vAi9nwiYQGe4mkTdBwFHZlHTFjz5j8Celc= 68 | github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= 69 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 70 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 71 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 72 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 73 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 74 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 75 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 76 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 77 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 78 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 79 | github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= 80 | github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= 81 | github.com/btcsuitereleases/btcd v0.0.0-20150613002942-e005ff2cdaac/go.mod h1:3qavLoLVbhxVp2fb1RfKqhD0XC6WEm0MaW2Oku9EwVI= 82 | github.com/btcsuitereleases/btclog v0.0.0-20150612204814-9ccde63ae9b8/go.mod h1:O6/l+arU3TCAYEFvAvBbboS5NTPJub/IHqx6LaUXDBA= 83 | github.com/btcsuitereleases/btcrpcclient v0.0.0-20150612223923-30a5dc36b5f1/go.mod h1:0OiVP1jK6K/NQYWFFFNEoLr+QAoXIPIaJGU+gcZFIjo= 84 | github.com/btcsuitereleases/btcutil v0.0.0-20150612230727-f2b1058a8255 h1:2Dd/81Xn+6DGPIV01YTt9mNV1li0kM1dk62cE3YDU44= 85 | github.com/btcsuitereleases/btcutil v0.0.0-20150612230727-f2b1058a8255/go.mod h1:cUeoYJcc2EfS9DIrDrJ44AjirCbgkmThYeFu/yEddxs= 86 | github.com/btcsuitereleases/fastsha256 v0.0.0-20150409163857-302ad4db268b/go.mod h1:dIfwNvjyTTUDQQdqGNqDTZ0yUK4JroGNT9f5D7izMPY= 87 | github.com/btcsuitereleases/go-socks v0.0.0-20150513194711-cfe8b59e565c/go.mod h1:71FLiNMlftkp136Kcb9K2pvYO2cRjNT1p7xxTcCNPAA= 88 | github.com/btcsuitereleases/golangcrypto v0.0.0-20150501145153-05002d689021/go.mod h1:OURMPqO7Ta8TdN0KQcOWDo2dCcGTmORg76cfEjPp+Nw= 89 | github.com/btcsuitereleases/seelog v0.0.0-20150116041118-313961b101eb/go.mod h1:0iQJ1aoxBb/+Fe7wFlv20k7AddJTyLDbF8nYGHFQpbU= 90 | github.com/btcsuitereleases/websocket v0.0.0-20150501132526-4f61fd4eb661/go.mod h1:OwZqbMCkvueyUEZHgT4y/Pngd3EXWw6DfhuXaXpeYHw= 91 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 92 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 93 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 94 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 95 | github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw= 96 | github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= 97 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 98 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 99 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 100 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 101 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 102 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 103 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 104 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 105 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 106 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 107 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 108 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 109 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 110 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 111 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 112 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 113 | github.com/gogo/protobuf v1.3.1-0.20190908201246-8a5ed79f6888 h1:bzMQIeZA1YG0Ba2SySz4dIXsiQZ8B4wz0ukcKIpbUVU= 114 | github.com/gogo/protobuf v1.3.1-0.20190908201246-8a5ed79f6888/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 115 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 116 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 117 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 118 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 119 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 120 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 121 | github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= 122 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 123 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 124 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 125 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 126 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 127 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 128 | github.com/gorilla/mux v1.7.4-0.20190720201435-e67b3c02c719 h1:zQ+2G/ywb753CxKkvvZ0O6uu/fk5HRm21wdcVEq1U6E= 129 | github.com/gorilla/mux v1.7.4-0.20190720201435-e67b3c02c719/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 130 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 131 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 132 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd h1:rNuUHR+CvK1IS89MMtcF0EpcVMZtjKfPRp4MEmt/aTs= 133 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= 134 | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= 135 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 136 | github.com/hashicorp/go-plugin v1.3.0 h1:4d/wJojzvHV1I4i/rrjVaeuyxWrLzDE1mDCyDy8fXS8= 137 | github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= 138 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= 139 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 140 | github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= 141 | github.com/howeyc/fsnotify v0.9.0 h1:0gtV5JmOKH4A8SsFxG2BczSeXWWPvcMT0euZt5gDAxY= 142 | github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA= 143 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 144 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 145 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 146 | github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= 147 | github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= 148 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 149 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 150 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 151 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 152 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 153 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 154 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 155 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 156 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 157 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 158 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 159 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 160 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 161 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 162 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 163 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 164 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 165 | github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 166 | github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 167 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 168 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 169 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 170 | github.com/michaelbeam/cli v0.0.0-20151028184746-e3284e1e16c4 h1:XEYREMnqlY6Xo2c2eJR/1zqF9AOamJsvR17UR+bPMk0= 171 | github.com/michaelbeam/cli v0.0.0-20151028184746-e3284e1e16c4/go.mod h1:HianK/jxDAnOG41HbT3q9nrhC8dQnOVYfhCm/1GCe3U= 172 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg= 173 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 174 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 175 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 176 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 177 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 178 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 179 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 180 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 181 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 182 | github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= 183 | github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= 184 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 185 | github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= 186 | github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= 187 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 188 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 189 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 190 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 191 | github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= 192 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 193 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 194 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 195 | github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= 196 | github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 197 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 198 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 199 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 200 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 201 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 202 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 203 | github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= 204 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 205 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 206 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 207 | github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= 208 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 209 | github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= 210 | github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 211 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 212 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 213 | github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= 214 | github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= 215 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 216 | github.com/spf13/cobra v0.0.4-0.20180915222204-8d114be902bc/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 217 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 218 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 219 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 220 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 221 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 222 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 223 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 224 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 225 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 226 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 227 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 228 | golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 229 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 230 | golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA= 231 | golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 232 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 233 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 234 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 235 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 236 | golang.org/x/net v0.0.0-20161116075034-4971afdc2f16/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 237 | golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 238 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 239 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 240 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 241 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 242 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 243 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 244 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 245 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 246 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 247 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4= 248 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 249 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 250 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 251 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 252 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 253 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 254 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 255 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 256 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 257 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 258 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 259 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 260 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 261 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 262 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 263 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 264 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 265 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 266 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 267 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 268 | golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE= 269 | golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 270 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 271 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 272 | golang.org/x/text v0.3.1-0.20181010134911-4d1c5fb19474 h1:4l+CHZwCUFzGF11IlLbqggmpYvJyXOKSlGBZ8M0Ag/w= 273 | golang.org/x/text v0.3.1-0.20181010134911-4d1c5fb19474/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 274 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 275 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 276 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 277 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 278 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 279 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 280 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 281 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 282 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 283 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 284 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 285 | google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 286 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 287 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 288 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 289 | google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 290 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 291 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 292 | google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= 293 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 294 | gopkg.in/AlecAivazis/survey.v1 v1.6.2/go.mod h1:2Ehl7OqkBl3Xb8VmC4oFW2bItAhnUfzIjrOzwRxCrOU= 295 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 296 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 297 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 298 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 299 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 300 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 301 | gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= 302 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 303 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 304 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 305 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 306 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 307 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 308 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 309 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 310 | gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= 311 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 312 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 313 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 314 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 315 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 316 | launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= 317 | launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= 318 | -------------------------------------------------------------------------------- /help.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "regexp" 11 | "sort" 12 | "strings" 13 | ) 14 | 15 | type helper struct { 16 | topics map[string]*fctCmd 17 | } 18 | 19 | // NewHelper creates a new helper object containing the help messages for a set 20 | // of commands. 21 | func newHelper() *helper { 22 | h := new(helper) 23 | h.topics = make(map[string]*fctCmd) 24 | return h 25 | } 26 | 27 | func (h *helper) Add(s string, c *fctCmd) { 28 | h.topics[s] = c 29 | } 30 | 31 | func (h *helper) All() { 32 | flag.VisitAll(func(f *flag.Flag) { 33 | m, err := regexp.MatchString("^test", f.Name) 34 | if err != nil { 35 | errorln(err) 36 | } 37 | if !m { 38 | fmt.Printf("%s\n\t%s\n\n", "-"+f.Name, f.Usage) 39 | } 40 | }) 41 | 42 | keys := make([]string, 0) 43 | for k := range h.topics { 44 | keys = append(keys, k) 45 | } 46 | 47 | sort.Strings(keys) 48 | 49 | for _, v := range keys { 50 | fmt.Printf("%s\n\t%s\n\n", h.topics[v].helpMsg, h.topics[v].description) 51 | } 52 | } 53 | 54 | func (h *helper) Execute(args []string) { 55 | if len(args) < 1 { 56 | fmt.Println("factom-cli help [subcommand]") 57 | return 58 | } 59 | 60 | if args[0] == "help" { 61 | if len(args) == 1 { 62 | help.All() 63 | return 64 | } 65 | args = args[1:] 66 | } 67 | 68 | topic := strings.Join(args[:], " ") 69 | 70 | c, ok := h.topics[topic] 71 | if !ok { 72 | if c, ok = h.topics[args[0]]; !ok { 73 | fmt.Println("No help for:", topic) 74 | return 75 | } 76 | } 77 | fmt.Printf("%s\n\t%s\n", c.helpMsg, c.description) 78 | } 79 | 80 | var help = newHelper() 81 | -------------------------------------------------------------------------------- /help_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_newHelper(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | want *helper 12 | }{ 13 | // TODO: Add test cases. 14 | } 15 | for _, tt := range tests { 16 | t.Run(tt.name, func(t *testing.T) { 17 | if got := newHelper(); !reflect.DeepEqual(got, tt.want) { 18 | t.Errorf("newHelper() = %v, want %v", got, tt.want) 19 | } 20 | }) 21 | } 22 | } 23 | 24 | func Test_helper_Add(t *testing.T) { 25 | type fields struct { 26 | topics map[string]*fctCmd 27 | } 28 | type args struct { 29 | s string 30 | c *fctCmd 31 | } 32 | tests := []struct { 33 | name string 34 | fields fields 35 | args args 36 | }{ 37 | // TODO: Add test cases. 38 | } 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | h := &helper{ 42 | topics: tt.fields.topics, 43 | } 44 | h.Add(tt.args.s, tt.args.c) 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /identityKeys.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | 12 | "github.com/FactomProject/factom" 13 | "github.com/posener/complete" 14 | ) 15 | 16 | // newIdentityKey generates a new identity key in the wallet 17 | var newIdentityKey = func() *fctCmd { 18 | cmd := new(fctCmd) 19 | cmd.helpMsg = "factom-cli newidentitykey" 20 | cmd.description = "Generate a new identity key in the wallet" 21 | cmd.execFunc = func(args []string) { 22 | os.Args = args 23 | flag.Parse() 24 | args = flag.Args() 25 | 26 | k, err := factom.GenerateIdentityKey() 27 | if err != nil { 28 | errorln(err) 29 | return 30 | } 31 | fmt.Println(k.PubString()) 32 | } 33 | help.Add("newidentitykey", cmd) 34 | return cmd 35 | }() 36 | 37 | // importIdentityKeys imports identity keys from 1 or more secret keys into the wallet 38 | var importIdentityKeys = func() *fctCmd { 39 | cmd := new(fctCmd) 40 | cmd.helpMsg = "factom-cli importidentitykeys SECKEY [SECKEY...]" 41 | cmd.description = "Import one or more identity keys into the wallet from the specified idsec keys" 42 | cmd.execFunc = func(args []string) { 43 | os.Args = args 44 | flag.Parse() 45 | args = flag.Args() 46 | 47 | if len(args) < 1 { 48 | fmt.Println(cmd.helpMsg) 49 | return 50 | } 51 | keys, err := factom.ImportIdentityKeys(args...) 52 | if err != nil { 53 | errorln(err) 54 | return 55 | } 56 | for _, k := range keys { 57 | fmt.Println(k) 58 | } 59 | } 60 | help.Add("importidentitykeys", cmd) 61 | return cmd 62 | }() 63 | 64 | // exportIdentityKeys lists the identity key pairs (public and private) stored in the wallet 65 | var exportIdentityKeys = func() *fctCmd { 66 | cmd := new(fctCmd) 67 | cmd.helpMsg = "factom-cli exportidentitykeys" 68 | cmd.description = "List the identity key pairs (public and private) stored in the wallet" 69 | cmd.execFunc = func(args []string) { 70 | os.Args = args 71 | flag.Parse() 72 | args = flag.Args() 73 | 74 | keys, err := factom.FetchIdentityKeys() 75 | if err != nil { 76 | errorln(err) 77 | return 78 | } 79 | for _, k := range keys { 80 | fmt.Println(k.SecString(), k.PubString()) 81 | } 82 | } 83 | help.Add("exportidentitykeys", cmd) 84 | return cmd 85 | }() 86 | 87 | // listIdentityKeys lists the addresses in the wallet 88 | var listIdentityKeys = func() *fctCmd { 89 | cmd := new(fctCmd) 90 | cmd.helpMsg = "factom-cli listidentitykeys" 91 | cmd.description = "List the public identity keys stored in the wallet" 92 | cmd.execFunc = func(args []string) { 93 | os.Args = args 94 | flag.Parse() 95 | args = flag.Args() 96 | 97 | keys, err := factom.FetchIdentityKeys() 98 | if err != nil { 99 | errorln(err) 100 | return 101 | } 102 | 103 | for _, k := range keys { 104 | fmt.Println(k.PubString()) 105 | } 106 | } 107 | help.Add("listidentitykeys", cmd) 108 | return cmd 109 | }() 110 | 111 | // removeIdentityKey removes an identity key pair from the wallet 112 | var removeIdentityKey = func() *fctCmd { 113 | cmd := new(fctCmd) 114 | cmd.helpMsg = "factom-cli rmidentitykey PUBKEY" 115 | cmd.description = "Removes the identity key pair from the wallet for the specified idpub key." 116 | cmd.completion = complete.Command{ 117 | Args: predictIdentityKey, 118 | } 119 | cmd.execFunc = func(args []string) { 120 | if len(args) < 2 { 121 | fmt.Println(cmd.helpMsg) 122 | return 123 | } 124 | pub := args[1] 125 | 126 | err := factom.RemoveIdentityKey(pub) 127 | if err != nil { 128 | fmt.Printf("%v\n", err) 129 | return 130 | } 131 | } 132 | help.Add("rmidentitykey", cmd) 133 | return cmd 134 | }() 135 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | 12 | "github.com/FactomProject/cli" 13 | "github.com/FactomProject/factom" 14 | "github.com/FactomProject/factomd/util" 15 | "github.com/posener/complete" 16 | ) 17 | 18 | // Version of factom-cli 19 | // FactomcliVersion sets the semantic version number of the build 20 | // $ go install -ldflags "-X main.FactomcliVersion=`cat VERSION`" -v 21 | // It also seems to need to have the previous binary deleted if recompiling to have this message show up if no code has changed. 22 | 23 | var FactomcliVersion string = "BuiltWithoutVersion" 24 | 25 | func main() { 26 | var ( 27 | walletRpcUser = flag.String( 28 | "walletuser", 29 | "", 30 | "Username for API connections to factom-walletd", 31 | ) 32 | walletRpcPassword = flag.String( 33 | "walletpassword", 34 | "", 35 | "Password for API connections to factom-walletd", 36 | ) 37 | factomdRpcUser = flag.String( 38 | "factomduser", 39 | "", 40 | "Username for API connections to factomd", 41 | ) 42 | factomdRpcPassword = flag.String( 43 | "factomdpassword", 44 | "", 45 | "Password for API connections to factomd", 46 | ) 47 | factomdLocation = flag.String( 48 | "s", 49 | "", 50 | "IPAddr:port# of factomd API to use to access blockchain (default"+ 51 | " localhost:8088)", 52 | ) 53 | walletdLocation = flag.String( 54 | "w", 55 | "", 56 | "IPAddr:port# of factom-walletd API to use to create transactions"+ 57 | " (default localhost:8089)", 58 | ) 59 | walletTLSflag = flag.Bool( 60 | "wallettls", 61 | false, 62 | "Set to true when the wallet API is encrypted", 63 | ) 64 | walletTLSCert = flag.String( 65 | "walletcert", 66 | "", 67 | "This file is the TLS certificate provided by the factom-walletd"+ 68 | " API. (default ~/.factom/walletAPIpub.cert)", 69 | ) 70 | factomdTLSflag = flag.Bool( 71 | "factomdtls", 72 | false, 73 | "Set to true when the factomd API is encrypted", 74 | ) 75 | factomdTLSCert = flag.String( 76 | "factomdcert", 77 | "", 78 | "This file is the TLS certificate provided by the factomd API."+ 79 | " (default ~/.factom/m2/factomdAPIpub.cert)", 80 | ) 81 | ) 82 | 83 | // setup cli autocomplete for the shell. Run `$ factom-cli -complete` to 84 | // install the completion in the local environment. 85 | cliCompletion := complete.New("factom-cli", complete.Command{ 86 | Sub: complete.Commands{ 87 | "addchain": addchain.completion, 88 | "addentry": addentry.completion, 89 | "backupwallet": backupwallet.completion, 90 | "balance": balance.completion, 91 | "balancetotals": balancetotals.completion, 92 | "composechain": composechain.completion, 93 | "composeentry": composeentry.completion, 94 | "diagnostics": diagnostics.completion, 95 | "ecrate": complete.Command{}, 96 | "exportaddresses": complete.Command{}, 97 | "get": get.completion, 98 | "importaddresses": complete.Command{}, 99 | "importkoinify": complete.Command{}, 100 | "listaddresses": complete.Command{}, 101 | "newecaddress": complete.Command{}, 102 | "newfctaddress": complete.Command{}, 103 | "properties": properties.completion, 104 | "receipt": complete.Command{}, 105 | "rmaddress": removeAddress.completion, 106 | "status": status.completion, 107 | "unlockwallet": unlockwallet.completion, 108 | "replaydbstates": replaydbstates.completion, 109 | 110 | "newtx": newtx.completion, 111 | "rmtx": rmtx.completion, 112 | "listtxs": listtxs.completion, 113 | "addtxinput": addtxinput.completion, 114 | "addtxoutput": addtxoutput.completion, 115 | "addtxecoutput": addtxecoutput.completion, 116 | "addtxfee": addtxfee.completion, 117 | "subtxfee": subtxfee.completion, 118 | "signtx": signtx.completion, 119 | "composetx": complete.Command{}, 120 | "sendtx": sendtx.completion, 121 | "sendfct": sendfct.completion, 122 | "buyec": buyec.completion, 123 | 124 | "newidentitykey": complete.Command{}, 125 | "importidentitykeys": complete.Command{}, 126 | "exportidentitykeys": complete.Command{}, 127 | "listidentitykeys": complete.Command{}, 128 | "rmidentitykey": removeIdentityKey.completion, 129 | "identity": identity.completion, 130 | }, 131 | }) 132 | cliCompletion.CLI.InstallName = "complete" 133 | cliCompletion.CLI.UninstallName = "uncomplete" 134 | cliCompletion.AddFlags(nil) 135 | 136 | flag.Parse() 137 | 138 | if cliCompletion.Complete() { 139 | return 140 | } 141 | 142 | // see if the config file has values which should be used instead of null 143 | // strings 144 | filename := util.ConfigFilename() 145 | 146 | // instead of giving warnings, check that the file exists before attempting 147 | // to read it. 148 | if _, err := os.Stat(filename); err == nil { 149 | cfg := util.ReadConfig(filename) 150 | 151 | if *walletRpcUser == "" { 152 | if cfg.Walletd.WalletRpcUser != "" { 153 | *walletRpcUser = cfg.Walletd.WalletRpcUser 154 | *walletRpcPassword = cfg.Walletd.WalletRpcPass 155 | } 156 | } 157 | 158 | if *factomdRpcUser == "" { 159 | if cfg.App.FactomdRpcUser != "" { 160 | *factomdRpcUser = cfg.App.FactomdRpcUser 161 | *factomdRpcPassword = cfg.App.FactomdRpcPass 162 | } 163 | } 164 | 165 | if *factomdLocation == "" { 166 | if cfg.Walletd.FactomdLocation != "localhost:8088" { 167 | *factomdLocation = cfg.Walletd.FactomdLocation 168 | } 169 | } 170 | 171 | if *walletdLocation == "" { 172 | if cfg.Walletd.WalletdLocation != "localhost:8089" { 173 | *walletdLocation = cfg.Walletd.WalletdLocation 174 | } 175 | } 176 | 177 | // if a config file is found, and the wallet will start with TLS, 178 | // factom-cli should use TLS too 179 | if cfg.Walletd.WalletTlsEnabled == true { 180 | *walletTLSflag = true 181 | } 182 | 183 | // if specified on the command line, don't use the config file 184 | if *walletTLSCert == "" { 185 | //otherwise check if the the config file has something new 186 | if cfg.Walletd.WalletTlsPublicCert != "/full/path/to/walletAPIpub.cert" { 187 | *walletTLSCert = cfg.Walletd.WalletTlsPublicCert 188 | } 189 | } 190 | 191 | // if a config file is found, and the factomd will start with TLS, 192 | // factom-cli should use TLS too 193 | if cfg.App.FactomdTlsEnabled == true { 194 | *factomdTLSflag = true 195 | } 196 | 197 | //if specified on the command line, don't use the config file 198 | if *factomdTLSCert == "" { 199 | //otherwise check if the the config file has something new 200 | if cfg.App.FactomdTlsPublicCert != "/full/path/to/factomdAPIpub.cert" { 201 | *factomdTLSCert = cfg.App.FactomdTlsPublicCert 202 | } 203 | } 204 | } 205 | 206 | // if all defaults were specified on both the command line and config file 207 | if *walletTLSCert == "" { 208 | *walletTLSCert = fmt.Sprint( 209 | util.GetHomeDir(), 210 | "/.factom/walletAPIpub.cert", 211 | ) 212 | } 213 | 214 | // if all defaults were specified on both the command line and config file 215 | if *factomdTLSCert == "" { 216 | *factomdTLSCert = fmt.Sprint( 217 | util.GetHomeDir(), 218 | "/.factom/m2/factomdAPIpub.cert", 219 | ) 220 | } 221 | 222 | // set the default if a config file doesn't exist 223 | if *factomdLocation == "" { 224 | *factomdLocation = "localhost:8088" 225 | } 226 | if *walletdLocation == "" { 227 | *walletdLocation = "localhost:8089" 228 | } 229 | 230 | args := flag.Args() 231 | 232 | factom.SetFactomdServer(*factomdLocation) 233 | factom.SetWalletServer(*walletdLocation) 234 | factom.SetFactomdRpcConfig(*factomdRpcUser, *factomdRpcPassword) 235 | factom.SetWalletRpcConfig(*walletRpcUser, *walletRpcPassword) 236 | factom.SetWalletEncryption(*walletTLSflag, *walletTLSCert) 237 | factom.SetFactomdEncryption(*factomdTLSflag, *factomdTLSCert) 238 | 239 | c := cli.New() 240 | c.Handle("help", help) 241 | c.Handle("addchain", addchain) 242 | c.Handle("addentry", addentry) 243 | c.Handle("backupwallet", backupwallet) 244 | c.Handle("balance", balance) 245 | c.Handle("balancetotals", balancetotals) 246 | c.Handle("composechain", composechain) 247 | c.Handle("composeentry", composeentry) 248 | c.Handle("diagnostics", diagnostics) 249 | c.Handle("ecrate", ecrate) 250 | c.Handle("exportaddresses", exportaddresses) 251 | c.Handle("get", get) 252 | c.Handle("importaddress", importaddresses) 253 | c.Handle("importkoinify", importkoinify) 254 | c.Handle("listaddresses", listaddresses) 255 | c.Handle("newecaddress", newecaddress) 256 | c.Handle("newfctaddress", newfctaddress) 257 | c.Handle("properties", properties) 258 | c.Handle("receipt", receipt) 259 | c.Handle("rmaddress", removeAddress) 260 | c.Handle("status", status) 261 | c.Handle("unlockwallet", unlockwallet) 262 | c.Handle("replaydbstates", replaydbstates) 263 | 264 | // transaction commands 265 | c.Handle("newtx", newtx) 266 | c.Handle("rmtx", rmtx) 267 | c.Handle("listtxs", listtxs) 268 | c.Handle("addtxinput", addtxinput) 269 | c.Handle("addtxoutput", addtxoutput) 270 | c.Handle("addtxecoutput", addtxecoutput) 271 | c.Handle("addtxfee", addtxfee) 272 | c.Handle("subtxfee", subtxfee) 273 | c.Handle("signtx", signtx) 274 | c.Handle("composetx", composetx) 275 | c.Handle("sendtx", sendtx) 276 | c.Handle("sendfct", sendfct) 277 | c.Handle("buyec", buyec) 278 | 279 | // identity commands 280 | c.Handle("newidentitykey", newIdentityKey) 281 | c.Handle("importidentitykeys", importIdentityKeys) 282 | c.Handle("exportidentitykeys", exportIdentityKeys) 283 | c.Handle("listidentitykeys", listIdentityKeys) 284 | c.Handle("rmidentitykey", removeIdentityKey) 285 | c.Handle("identity", identity) 286 | 287 | c.HandleDefault(help) 288 | c.Execute(args) 289 | } 290 | 291 | func errorln(a ...interface{}) (n int, err error) { 292 | return fmt.Fprintln(os.Stderr, a...) 293 | } 294 | -------------------------------------------------------------------------------- /raw.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/hex" 9 | "fmt" 10 | "strings" 11 | 12 | "github.com/FactomProject/factom" 13 | ) 14 | 15 | var getraw = func() *fctCmd { 16 | cmd := new(fctCmd) 17 | cmd.helpMsg = "factom-cli get raw HASH" 18 | cmd.description = "Returns a raw hex representation of a block, transaction, entry, or commit" 19 | cmd.execFunc = func(args []string) { 20 | if len(args) < 2 { 21 | fmt.Println(cmd.helpMsg) 22 | return 23 | } 24 | 25 | h := args[1] 26 | hx, err := hex.DecodeString(strings.Replace(h, "\"", "", -1)) 27 | if err != nil { 28 | errorln("Error reading hash") 29 | return 30 | } 31 | if len(hx) != 32 { 32 | errorln("Invalid argument length - should be 64 characters (32 bytes) long") 33 | return 34 | } 35 | 36 | raw, err := factom.GetRaw(fmt.Sprintf("%x", hx)) 37 | if err != nil { 38 | errorln(err) 39 | return 40 | } 41 | if len(raw) > 0 { 42 | fmt.Printf("%x\n", raw) 43 | return 44 | } 45 | 46 | fmt.Printf("Block, transaction or entry not found.\n") 47 | } 48 | help.Add("get raw", cmd) 49 | return cmd 50 | }() 51 | -------------------------------------------------------------------------------- /receipt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/json" 9 | "flag" 10 | "fmt" 11 | "os" 12 | 13 | "github.com/FactomProject/factom" 14 | ) 15 | 16 | var receipt = func() *fctCmd { 17 | cmd := new(fctCmd) 18 | cmd.helpMsg = "factom-cli receipt ENTRYHASH" 19 | cmd.description = "Returns a Receipt for a given Entry" 20 | cmd.execFunc = func(args []string) { 21 | os.Args = args 22 | flag.Parse() 23 | args = flag.Args() 24 | 25 | if len(args) != 1 { 26 | fmt.Println(cmd.helpMsg) 27 | return 28 | } 29 | txID := args[0] 30 | 31 | resp, err := factom.GetReceipt(txID) 32 | if err != nil { 33 | errorln(err) 34 | return 35 | } 36 | str, err := json.MarshalIndent(resp, "", "\t") 37 | if err != nil { 38 | errorln(err) 39 | return 40 | } 41 | fmt.Printf("%s\n", str) 42 | } 43 | help.Add("receipt", cmd) 44 | return cmd 45 | }() 46 | -------------------------------------------------------------------------------- /replaystates.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | "strconv" 12 | 13 | "github.com/FactomProject/factom" 14 | ) 15 | 16 | var replaydbstates = func() *fctCmd { 17 | cmd := new(fctCmd) 18 | cmd.helpMsg = "factom-cli replaydbstates STARTHEIGHT ENDHEIGHT" 19 | cmd.description = "Emit DBStateMsgs over the LiveFeed API between two specifed block heights" 20 | cmd.execFunc = func(args []string) { 21 | os.Args = args 22 | flag.Parse() 23 | args = flag.Args() 24 | if len(args) < 1 || len(args) > 2 { 25 | fmt.Println(cmd.helpMsg) 26 | return 27 | } 28 | 29 | startHeightStr, err := strconv.ParseInt(args[0], 10, 32) 30 | if err != nil { 31 | errorln(err) 32 | return 33 | } 34 | startheight := startHeightStr 35 | 36 | var endheight int64 37 | endheight = 0 38 | if len(args) == 2 { 39 | endHeightStr, err := strconv.ParseInt(args[1], 10, 32) 40 | if err != nil { 41 | errorln(err) 42 | return 43 | } 44 | endheight = endHeightStr 45 | } 46 | 47 | res, err := factom.ReplayDBlockFromHeight(startheight, endheight) 48 | if err != nil { 49 | errorln(err) 50 | return 51 | } 52 | 53 | fmt.Println(res.Message) 54 | } 55 | help.Add("replaydbstates", cmd) 56 | return cmd 57 | }() 58 | -------------------------------------------------------------------------------- /transaction.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | "strconv" 12 | 13 | "github.com/FactomProject/cli" 14 | "github.com/FactomProject/factom" 15 | "github.com/posener/complete" 16 | ) 17 | 18 | // newtx creates a new transaction in the wallet. 19 | var newtx = func() *fctCmd { 20 | cmd := new(fctCmd) 21 | cmd.helpMsg = "factom-cli newtx [-q] TXNAME" 22 | cmd.description = "Create a new transaction in the wallet. -q quiet." 23 | cmd.completion = complete.Command{ 24 | Flags: complete.Flags{ 25 | "-q": complete.PredictNothing, 26 | }, 27 | Args: predictTxName, 28 | } 29 | cmd.execFunc = func(args []string) { 30 | os.Args = args 31 | qflag := flag.Bool("q", false, "quiet mode; no output") 32 | flag.Parse() 33 | args = flag.Args() 34 | 35 | if len(args) != 1 { 36 | fmt.Println(cmd.helpMsg) 37 | return 38 | } 39 | 40 | tx, err := factom.NewTransaction(args[0]) 41 | if err != nil { 42 | errorln(err) 43 | return 44 | } 45 | 46 | // output 47 | switch { 48 | // quiet mode; don't print anything 49 | case *qflag: 50 | default: 51 | fmt.Println(tx) 52 | } 53 | } 54 | help.Add("newtx", cmd) 55 | return cmd 56 | }() 57 | 58 | // rmtx removes a transaction in the wallet. 59 | var rmtx = func() *fctCmd { 60 | cmd := new(fctCmd) 61 | cmd.helpMsg = "factom-cli rmtx TXNAME" 62 | cmd.description = "Remove a transaction in the wallet" 63 | cmd.completion = complete.Command{ 64 | Args: predictTxName, 65 | } 66 | cmd.execFunc = func(args []string) { 67 | os.Args = args 68 | flag.Parse() 69 | args = flag.Args() 70 | 71 | if len(args) != 1 { 72 | fmt.Println(cmd.helpMsg) 73 | return 74 | } 75 | if err := factom.DeleteTransaction(args[0]); err != nil { 76 | errorln(err) 77 | return 78 | } 79 | } 80 | help.Add("rmtx", cmd) 81 | return cmd 82 | }() 83 | 84 | // addtxinput adds a factoid input to a transaction in the wallet. 85 | var addtxinput = func() *fctCmd { 86 | cmd := new(fctCmd) 87 | cmd.helpMsg = "factom-cli addtxinput [-q] TXNAME ADDRESS AMOUNT" 88 | cmd.description = "Add a Factoid input to a transaction in the wallet. -q" + 89 | " quiet." 90 | cmd.completion = complete.Command{ 91 | Flags: complete.Flags{ 92 | "-q": complete.PredictNothing, 93 | }, 94 | Args: complete.PredictFunc(func(args complete.Args) []string { 95 | // predict an address or a transaction name 96 | s := make([]string, 0) 97 | for _, a := range predictAddress.Predict(args) { 98 | s = append(s, a) 99 | } 100 | for _, n := range predictTxName.Predict(args) { 101 | s = append(s, n) 102 | } 103 | return s 104 | }), 105 | } 106 | cmd.execFunc = func(args []string) { 107 | os.Args = args 108 | qflag := flag.Bool("q", false, "quiet mode; no output") 109 | flag.Parse() 110 | args = flag.Args() 111 | if len(args) != 3 { 112 | fmt.Println(cmd.helpMsg) 113 | return 114 | } 115 | 116 | tx, err := factom.AddTransactionInput( 117 | args[0], 118 | args[1], 119 | factom.FactoidToFactoshi(args[2]), 120 | ) 121 | if err != nil { 122 | errorln(err) 123 | return 124 | } 125 | 126 | // output 127 | switch { 128 | // quiet mode; don't print anything 129 | case *qflag: 130 | default: 131 | fmt.Println(tx) 132 | } 133 | } 134 | help.Add("addtxinput", cmd) 135 | return cmd 136 | }() 137 | 138 | // addtxoutput adds a factoid output to a transaction in the wallet. 139 | var addtxoutput = func() *fctCmd { 140 | cmd := new(fctCmd) 141 | cmd.helpMsg = "factom-cli addtxoutput [-rq] TXNAME ADDRESS AMOUNT" 142 | cmd.description = "Add a Factoid output to a transaction in the wallet." + 143 | " -r Netki DNS resolve. -q quiet." 144 | cmd.completion = complete.Command{ 145 | Flags: complete.Flags{ 146 | "-r": complete.PredictNothing, 147 | "-q": complete.PredictNothing, 148 | }, 149 | Args: complete.PredictFunc(func(args complete.Args) []string { 150 | // predict an address or a transaction name 151 | s := make([]string, 0) 152 | for _, a := range predictAddress.Predict(args) { 153 | s = append(s, a) 154 | } 155 | for _, n := range predictTxName.Predict(args) { 156 | s = append(s, n) 157 | } 158 | return s 159 | }), 160 | } 161 | cmd.execFunc = func(args []string) { 162 | os.Args = args 163 | res := flag.Bool("r", false, "resolve DNS address from Netki") 164 | qflag := flag.Bool("q", false, "quiet mode; no output") 165 | flag.Parse() 166 | args = flag.Args() 167 | if len(args) != 3 { 168 | fmt.Println(cmd.helpMsg) 169 | return 170 | } 171 | 172 | out := args[1] 173 | if *res { 174 | if f, _, err := factom.ResolveDnsName(args[1]); err != nil { 175 | errorln(err) 176 | return 177 | } else if f == "" { 178 | errorln("could not resolve factoid address") 179 | } else { 180 | out = f 181 | } 182 | } 183 | 184 | tx, err := factom.AddTransactionOutput( 185 | args[0], 186 | out, 187 | factom.FactoidToFactoshi(args[2]), 188 | ) 189 | if err != nil { 190 | errorln(err) 191 | return 192 | } 193 | 194 | // output 195 | switch { 196 | // quiet mode; don't print anything 197 | case *qflag: 198 | default: 199 | fmt.Println(tx) 200 | } 201 | } 202 | help.Add("addtxoutput", cmd) 203 | return cmd 204 | }() 205 | 206 | // addtxecoutput adds an entry credit output to a transaction in the wallet. 207 | var addtxecoutput = func() *fctCmd { 208 | cmd := new(fctCmd) 209 | cmd.helpMsg = "factom-cli addtxecoutput [-rq] TXNAME ADDRESS AMOUNT" 210 | cmd.description = "Add an Entry Credit output to a transaction in the" + 211 | " wallet. -r Netki DNS resolve. -q quiet." 212 | cmd.completion = complete.Command{ 213 | Flags: complete.Flags{ 214 | "-r": complete.PredictNothing, 215 | "-q": complete.PredictNothing, 216 | }, 217 | Args: complete.PredictFunc(func(args complete.Args) []string { 218 | // predict an address or a transaction name 219 | s := make([]string, 0) 220 | for _, a := range predictAddress.Predict(args) { 221 | s = append(s, a) 222 | } 223 | for _, n := range predictTxName.Predict(args) { 224 | s = append(s, n) 225 | } 226 | return s 227 | }), 228 | } 229 | cmd.execFunc = func(args []string) { 230 | os.Args = args 231 | res := flag.Bool("r", false, "resolve DNS address from Netki") 232 | qflag := flag.Bool("q", false, "quiet mode; no output") 233 | flag.Parse() 234 | args = flag.Args() 235 | if len(args) != 3 { 236 | fmt.Println(cmd.helpMsg) 237 | return 238 | } 239 | 240 | out := args[1] 241 | if *res { 242 | if _, e, err := factom.ResolveDnsName(args[1]); err != nil { 243 | errorln(err) 244 | return 245 | } else if e == "" { 246 | errorln("could not resolve entry credit address") 247 | } else { 248 | out = e 249 | } 250 | } 251 | 252 | tx, err := factom.AddTransactionECOutput( 253 | args[0], 254 | out, 255 | factom.FactoidToFactoshi(args[2]), 256 | ) 257 | if err != nil { 258 | errorln(err) 259 | return 260 | } 261 | 262 | // output 263 | switch { 264 | // quiet mode; don't print anything 265 | case *qflag: 266 | default: 267 | fmt.Println(tx) 268 | } 269 | } 270 | help.Add("addtxecoutput", cmd) 271 | return cmd 272 | }() 273 | 274 | // addtxfee adds an entry credit output to a transaction in the wallet. 275 | var addtxfee = func() *fctCmd { 276 | cmd := new(fctCmd) 277 | cmd.helpMsg = "factom-cli addtxfee [-q] TXNAME ADDRESS" 278 | cmd.description = "Add the transaction fee to an input of a transaction" + 279 | " in the wallet. -q quiet." 280 | cmd.completion = complete.Command{ 281 | Flags: complete.Flags{ 282 | "-q": complete.PredictNothing, 283 | }, 284 | Args: complete.PredictFunc(func(args complete.Args) []string { 285 | // predict an address or a transaction name 286 | s := make([]string, 0) 287 | for _, a := range predictAddress.Predict(args) { 288 | s = append(s, a) 289 | } 290 | for _, n := range predictTxName.Predict(args) { 291 | s = append(s, n) 292 | } 293 | return s 294 | }), 295 | } 296 | cmd.execFunc = func(args []string) { 297 | os.Args = args 298 | qflag := flag.Bool("q", false, "quiet mode; no output") 299 | flag.Parse() 300 | args = flag.Args() 301 | 302 | if len(args) != 2 { 303 | fmt.Println(cmd.helpMsg) 304 | return 305 | } 306 | 307 | tx, err := factom.AddTransactionFee(args[0], args[1]) 308 | if err != nil { 309 | errorln(err) 310 | return 311 | } 312 | 313 | // output 314 | switch { 315 | // quiet mode; don't print anything 316 | case *qflag: 317 | default: 318 | fmt.Println(tx) 319 | } 320 | } 321 | help.Add("addtxfee", cmd) 322 | return cmd 323 | }() 324 | 325 | // listtxs lists transactions from the wallet or the Factoid Chain. 326 | var listtxs = func() *fctCmd { 327 | cmd := new(fctCmd) 328 | cmd.helpMsg = "factom-cli listtxs [address|all|id|name|tmp|range]" 329 | cmd.description = "List transactions from the wallet or the Factoid Chain" 330 | cmd.completion = complete.Command{ 331 | Sub: complete.Commands{ 332 | "address": listtxsaddress.completion, 333 | "all": listtxsall.completion, 334 | "id": listtxsid.completion, 335 | "name": listtxsname.completion, 336 | "tmp": complete.Command{}, 337 | "range": listtxsrange.completion, 338 | }, 339 | } 340 | cmd.execFunc = func(args []string) { 341 | if len(args) > 1 { 342 | args = args[1:] 343 | } 344 | 345 | c := cli.New() 346 | c.Handle("all", listtxsall) 347 | c.Handle("address", listtxsaddress) 348 | c.Handle("id", listtxsid) 349 | c.Handle("range", listtxsrange) 350 | c.Handle("tmp", listtxstmp) 351 | c.Handle("name", listtxsname) 352 | c.HandleDefaultFunc(func(args []string) { 353 | if args[0] == "listtxs" { 354 | args[0] = "all" 355 | } else { 356 | args = append([]string{"all"}, args...) 357 | } 358 | listtxsall.execFunc(args) 359 | }) 360 | c.Execute(args) 361 | } 362 | help.Add("listtxs", cmd) 363 | return cmd 364 | }() 365 | 366 | // listtxsall lists all transactions from the Factoid Chain 367 | var listtxsall = func() *fctCmd { 368 | cmd := new(fctCmd) 369 | cmd.helpMsg = "factom-cli listtxs [all] [-T]" 370 | cmd.description = "List all transactions from the Factoid Chain. -T TxID." 371 | cmd.completion = complete.Command{ 372 | Flags: complete.Flags{ 373 | "-T": complete.PredictNothing, 374 | }, 375 | } 376 | cmd.execFunc = func(args []string) { 377 | os.Args = args 378 | tdisp := flag.Bool("T", false, "display only the TxID") 379 | flag.Parse() 380 | args = flag.Args() 381 | if len(args) > 0 { 382 | fmt.Println(cmd.helpMsg) 383 | return 384 | } 385 | 386 | txs, err := factom.ListTransactionsAll() 387 | if err != nil { 388 | errorln(err) 389 | return 390 | } 391 | for _, tx := range txs { 392 | switch { 393 | case *tdisp: 394 | fmt.Println(tx.TxID) 395 | default: 396 | fmt.Println(tx) 397 | } 398 | } 399 | } 400 | help.Add("listtxs all", cmd) 401 | return cmd 402 | }() 403 | 404 | // listtxsaddress lists transactions from the Factoid Chain with matching 405 | // address 406 | var listtxsaddress = func() *fctCmd { 407 | cmd := new(fctCmd) 408 | cmd.helpMsg = "factom-cli listtxs address [-T] ECADDRESS|FCTADDRESS" 409 | cmd.description = "List transaction from the Factoid Chain with a" + 410 | " specific address. -T TxID." 411 | cmd.completion = complete.Command{ 412 | Flags: complete.Flags{ 413 | "-T": complete.PredictNothing, 414 | }, 415 | Args: predictAddress, 416 | } 417 | cmd.execFunc = func(args []string) { 418 | os.Args = args 419 | tdisp := flag.Bool("T", false, "display only the TxID") 420 | flag.Parse() 421 | args = flag.Args() 422 | 423 | if len(args) < 1 { 424 | fmt.Println(cmd.helpMsg) 425 | return 426 | } 427 | 428 | txs, err := factom.ListTransactionsAddress(args[0]) 429 | if err != nil { 430 | errorln(err) 431 | return 432 | } 433 | for _, tx := range txs { 434 | switch { 435 | case *tdisp: 436 | fmt.Println(tx.TxID) 437 | default: 438 | fmt.Println(tx) 439 | } 440 | } 441 | } 442 | help.Add("listtxs address", cmd) 443 | return cmd 444 | }() 445 | 446 | // listtxsid lists transactions from the Factoid Chain with matching id 447 | var listtxsid = func() *fctCmd { 448 | cmd := new(fctCmd) 449 | cmd.helpMsg = "factom-cli listtxs id [-T] TXID" 450 | cmd.description = "List transaction from the Factoid Chain. -T TxID." 451 | cmd.completion = complete.Command{ 452 | Flags: complete.Flags{ 453 | "-T": complete.PredictNothing, 454 | }, 455 | } 456 | cmd.execFunc = func(args []string) { 457 | os.Args = args 458 | tdisp := flag.Bool("T", false, "display only the TxID") 459 | flag.Parse() 460 | args = flag.Args() 461 | if len(args) < 1 { 462 | fmt.Println(cmd.helpMsg) 463 | return 464 | } 465 | 466 | txs, err := factom.ListTransactionsID(args[0]) 467 | if err != nil { 468 | errorln(err) 469 | return 470 | } 471 | for _, tx := range txs { 472 | switch { 473 | case *tdisp: 474 | fmt.Println(tx.TxID) 475 | default: 476 | fmt.Println(tx) 477 | } 478 | } 479 | } 480 | help.Add("listtxs id", cmd) 481 | return cmd 482 | }() 483 | 484 | // listtxsname get a working transaction from the wallet. 485 | var listtxsname = func() *fctCmd { 486 | cmd := new(fctCmd) 487 | cmd.helpMsg = "factom-cli listtxs name [-T] TXNAME" 488 | cmd.description = "Show a current working transaction in the wallet. -T" + 489 | " TxID." 490 | cmd.completion = complete.Command{ 491 | Flags: complete.Flags{ 492 | "-T": complete.PredictNothing, 493 | }, 494 | Args: predictTxName, 495 | } 496 | cmd.execFunc = func(args []string) { 497 | os.Args = args 498 | tdisp := flag.Bool("T", false, "display transaction txid only") 499 | flag.Parse() 500 | args = flag.Args() 501 | 502 | if len(args) < 1 { 503 | fmt.Println(cmd.helpMsg) 504 | return 505 | } 506 | name := args[0] 507 | 508 | txs, err := factom.ListTransactionsTmp() 509 | if err != nil { 510 | errorln(err) 511 | return 512 | } 513 | for _, tx := range txs { 514 | if tx.Name == name { 515 | if *tdisp { 516 | fmt.Println(tx.TxID) 517 | } else { 518 | fmt.Println(tx) 519 | } 520 | } 521 | } 522 | } 523 | help.Add("listtxs name", cmd) 524 | return cmd 525 | }() 526 | 527 | // listtxsrange lists the transactions from the Factoid Chain within the 528 | // specified block height range 529 | var listtxsrange = func() *fctCmd { 530 | cmd := new(fctCmd) 531 | cmd.helpMsg = "factom-cli listtxs range [-T] START END" 532 | cmd.description = "List the transactions from the Factoid Chain within" + 533 | " the specified range. -T TxID." 534 | cmd.completion = complete.Command{ 535 | Flags: complete.Flags{ 536 | "-T": complete.PredictNothing, 537 | }, 538 | } 539 | cmd.execFunc = func(args []string) { 540 | os.Args = args 541 | tdisp := flag.Bool("T", false, "display only the TxID") 542 | flag.Parse() 543 | args = flag.Args() 544 | if len(args) < 2 { 545 | fmt.Println(cmd.helpMsg) 546 | return 547 | } 548 | 549 | start, err := strconv.Atoi(args[0]) 550 | if err != nil { 551 | errorln(err) 552 | return 553 | } 554 | end, err := strconv.Atoi(args[1]) 555 | if err != nil { 556 | errorln(err) 557 | return 558 | } 559 | 560 | txs, err := factom.ListTransactionsRange(start, end) 561 | if err != nil { 562 | errorln(err) 563 | return 564 | } 565 | for _, tx := range txs { 566 | switch { 567 | case *tdisp: 568 | fmt.Println(tx.TxID) 569 | default: 570 | fmt.Println(tx) 571 | } 572 | } 573 | } 574 | help.Add("listtxs range", cmd) 575 | return cmd 576 | }() 577 | 578 | // listtxstmp lists the working transactions in the wallet. 579 | var listtxstmp = func() *fctCmd { 580 | cmd := new(fctCmd) 581 | cmd.helpMsg = "factom-cli listtxs tmp" 582 | cmd.description = "List current working transactions in the wallet. -N" + 583 | " Names." 584 | cmd.execFunc = func(args []string) { 585 | os.Args = args 586 | ndisp := flag.Bool("N", false, "display transaction names only") 587 | flag.Parse() 588 | args = flag.Args() 589 | 590 | txs, err := factom.ListTransactionsTmp() 591 | if err != nil { 592 | errorln(err) 593 | return 594 | } 595 | for _, tx := range txs { 596 | if *ndisp { 597 | fmt.Println(tx.Name) 598 | } else { 599 | fmt.Println(tx) 600 | } 601 | } 602 | } 603 | help.Add("listtxs tmp", cmd) 604 | return cmd 605 | }() 606 | 607 | // subtxfee adds an entry credit output to a transaction in the wallet. 608 | var subtxfee = func() *fctCmd { 609 | cmd := new(fctCmd) 610 | cmd.helpMsg = "factom-cli subtxfee [-q] TXNAME ADDRESS" 611 | cmd.description = "Subtract the transaction fee from an output of a" + 612 | " transaction in the wallet. -q quiet." 613 | cmd.completion = complete.Command{ 614 | Flags: complete.Flags{ 615 | "-q": complete.PredictNothing, 616 | }, 617 | Args: complete.PredictFunc(func(args complete.Args) []string { 618 | // predict an address or a transaction name 619 | s := make([]string, 0) 620 | for _, a := range predictAddress.Predict(args) { 621 | s = append(s, a) 622 | } 623 | for _, n := range predictTxName.Predict(args) { 624 | s = append(s, n) 625 | } 626 | return s 627 | }), 628 | } 629 | cmd.execFunc = func(args []string) { 630 | os.Args = args 631 | qflag := flag.Bool("q", false, "quiet mode; no output") 632 | flag.Parse() 633 | args = flag.Args() 634 | if len(args) != 2 { 635 | fmt.Println(cmd.helpMsg) 636 | return 637 | } 638 | 639 | tx, err := factom.SubTransactionFee(args[0], args[1]) 640 | if err != nil { 641 | errorln(err) 642 | return 643 | } 644 | 645 | // output 646 | switch { 647 | // quiet mode; don't print anything 648 | case *qflag: 649 | default: 650 | fmt.Println(tx) 651 | } 652 | } 653 | help.Add("subtxfee", cmd) 654 | return cmd 655 | }() 656 | 657 | // signtx signs a transaction in the wallet 658 | var signtx = func() *fctCmd { 659 | cmd := new(fctCmd) 660 | cmd.helpMsg = "factom-cli signtx [-fqT] TXNAME" 661 | cmd.description = "Sign a transaction in the wallet. -q quiet. -T TxID." 662 | cmd.completion = complete.Command{ 663 | Flags: complete.Flags{ 664 | "-f": complete.PredictNothing, 665 | "-q": complete.PredictNothing, 666 | "-T": complete.PredictNothing, 667 | }, 668 | Args: predictTxName, 669 | } 670 | cmd.execFunc = func(args []string) { 671 | os.Args = args 672 | fflag := flag.Bool( 673 | "f", 674 | false, 675 | "force the transaction to be signed without fee or balance checks", 676 | ) 677 | qflag := flag.Bool("q", false, "quiet mode; no output") 678 | tdisp := flag.Bool("T", false, "display only the TxID") 679 | flag.Parse() 680 | args = flag.Args() 681 | if len(args) != 1 { 682 | fmt.Println(cmd.helpMsg) 683 | return 684 | } 685 | 686 | tx := new(factom.Transaction) 687 | t, err := factom.SignTransaction(args[0], *fflag) 688 | if err != nil { 689 | errorln(err) 690 | return 691 | } 692 | tx = t 693 | 694 | // output 695 | switch { 696 | // quiet mode; don't print anything 697 | case *qflag: 698 | case *tdisp: 699 | fmt.Println(tx.TxID) 700 | default: 701 | fmt.Println(tx) 702 | } 703 | } 704 | help.Add("signtx", cmd) 705 | return cmd 706 | }() 707 | 708 | // composetx composes the signed json rpc object to make a transaction against 709 | // factomd 710 | var composetx = func() *fctCmd { 711 | cmd := new(fctCmd) 712 | cmd.helpMsg = "factom-cli composetx TXNAME" 713 | cmd.description = "Compose a wallet transaction into a json rpc object" 714 | cmd.completion = complete.Command{ 715 | Args: predictTxName, 716 | } 717 | cmd.execFunc = func(args []string) { 718 | os.Args = args 719 | flag.Parse() 720 | args = flag.Args() 721 | 722 | if len(args) != 1 { 723 | fmt.Println(cmd.helpMsg) 724 | return 725 | } 726 | p, err := factom.ComposeTransaction(args[0]) 727 | if err != nil { 728 | errorln(err) 729 | return 730 | } 731 | 732 | factomdServer := GetFactomdServer() 733 | 734 | fmt.Println( 735 | "curl -X POST --data-binary", 736 | "'"+string(p)+"'", 737 | "-H 'content-type:text/plain;' http://"+factomdServer+"/v2", 738 | ) 739 | } 740 | help.Add("composetx", cmd) 741 | return cmd 742 | }() 743 | 744 | // sendtx composes and sends the signed transaction to factomd 745 | var sendtx = func() *fctCmd { 746 | cmd := new(fctCmd) 747 | cmd.helpMsg = "factom-cli sendtx [-fqT] TXNAME" 748 | cmd.description = "Send a Transaction to Factomd. -f force. -q quiet. -T TxID." 749 | cmd.completion = complete.Command{ 750 | Flags: complete.Flags{ 751 | "-f": complete.PredictNothing, 752 | "-q": complete.PredictNothing, 753 | "-T": complete.PredictNothing, 754 | }, 755 | Args: predictTxName, 756 | } 757 | cmd.execFunc = func(args []string) { 758 | os.Args = args 759 | fflag := flag.Bool( 760 | "f", 761 | false, 762 | "force the transaction to be sent without acknowledgement or"+ 763 | " balance checks", 764 | ) 765 | qflag := flag.Bool("q", false, "quiet mode; no output") 766 | tdisp := flag.Bool("T", false, "display only the TxID") 767 | flag.Parse() 768 | args = flag.Args() 769 | if len(args) != 1 { 770 | fmt.Println(cmd.helpMsg) 771 | return 772 | } 773 | 774 | tx, err := factom.SendTransaction(args[0]) 775 | if err != nil { 776 | errorln(err) 777 | return 778 | } 779 | // output 780 | switch { 781 | // quiet mode; don't print anything 782 | case *qflag: 783 | case *tdisp: 784 | fmt.Println(tx.TxID) 785 | default: 786 | fmt.Println("TxID:", tx.TxID) 787 | } 788 | 789 | // wait for the transaction to be acknowledged by the server 790 | if !*fflag { 791 | s, err := waitOnFctAck(tx.TxID) 792 | if err != nil { 793 | errorln(err) 794 | return 795 | } 796 | if !*qflag && !*tdisp { 797 | fmt.Println("Status:", s) 798 | } 799 | } 800 | } 801 | help.Add("sendtx", cmd) 802 | return cmd 803 | }() 804 | 805 | // sendfct sends factoids between 2 addresses 806 | var sendfct = func() *fctCmd { 807 | cmd := new(fctCmd) 808 | cmd.helpMsg = "factom-cli sendfct [-fqrT] FROMADDRESS TOADDRESS AMOUNT" 809 | cmd.description = "Send Factoids between 2 addresses. -f force. -q quiet." + 810 | " -r Netki DNS resolve. -T TxID." 811 | cmd.completion = complete.Command{ 812 | Flags: complete.Flags{ 813 | "-f": complete.PredictNothing, 814 | "-q": complete.PredictNothing, 815 | "-r": complete.PredictNothing, 816 | "-T": complete.PredictNothing, 817 | }, 818 | Args: predictAddress, 819 | } 820 | cmd.execFunc = func(args []string) { 821 | os.Args = args 822 | res := flag.Bool("r", false, "resolve dns address") 823 | fflag := flag.Bool( 824 | "f", 825 | false, 826 | "force the transaction to be sent without acknowledgement or"+ 827 | " balance checks", 828 | ) 829 | qflag := flag.Bool("q", false, "quiet mode; no output") 830 | tdisp := flag.Bool("T", false, "display only the TxID") 831 | flag.Parse() 832 | args = flag.Args() 833 | if len(args) != 3 { 834 | fmt.Println(cmd.helpMsg) 835 | return 836 | } 837 | 838 | tofc := args[1] 839 | 840 | // if -r flag is present resolve the ec address from the dns name. 841 | if *res { 842 | f, _, err := factom.ResolveDnsName(tofc) 843 | if err != nil { 844 | errorln(err) 845 | return 846 | } 847 | tofc = f 848 | } 849 | 850 | amt := factom.FactoidToFactoshi(args[2]) 851 | if amt == 0 { 852 | errorln("amount must be greater than 0") 853 | return 854 | } 855 | tx, err := factom.SendFactoid( 856 | args[0], 857 | tofc, 858 | amt, 859 | *fflag, 860 | ) 861 | if err != nil { 862 | errorln(err) 863 | return 864 | } 865 | 866 | switch { 867 | case *qflag: 868 | case *tdisp: 869 | fmt.Println(tx.TxID) 870 | default: 871 | fmt.Println("TxID:", tx.TxID) 872 | } 873 | 874 | // wait for the transaction to be acknowledged by the server 875 | if !*fflag { 876 | s, err := waitOnFctAck(tx.TxID) 877 | if err != nil { 878 | errorln(err) 879 | return 880 | } 881 | if !*qflag && !*tdisp { 882 | fmt.Println("Status:", s) 883 | } 884 | } 885 | } 886 | help.Add("sendfct", cmd) 887 | return cmd 888 | }() 889 | 890 | // buyec sends factoids between 2 addresses 891 | var buyec = func() *fctCmd { 892 | cmd := new(fctCmd) 893 | cmd.helpMsg = "factom-cli buyec [-fqrT] FCTADDRESS ECADDRESS ECAMOUNT" 894 | cmd.description = "Buy ECAMOUNT number of entry credits. -f force. " + 895 | "-q quiet. -r Netki DNS resolve. -T TxID." 896 | cmd.completion = complete.Command{ 897 | Flags: complete.Flags{ 898 | "-f": complete.PredictNothing, 899 | "-q": complete.PredictNothing, 900 | "-r": complete.PredictNothing, 901 | "-T": complete.PredictNothing, 902 | }, 903 | Args: predictAddress, 904 | } 905 | cmd.execFunc = func(args []string) { 906 | os.Args = args 907 | res := flag.Bool("r", false, "resolve dns address") 908 | fflag := flag.Bool( 909 | "f", 910 | false, 911 | "force the transaction to be sent without acknowledgement or"+ 912 | " balance checks", 913 | ) 914 | qflag := flag.Bool("q", false, "quiet mode; no output") 915 | tdisp := flag.Bool("T", false, "display only the TxID") 916 | flag.Parse() 917 | args = flag.Args() 918 | if len(args) != 3 { 919 | fmt.Println(cmd.helpMsg) 920 | return 921 | } 922 | 923 | toec := args[1] 924 | 925 | // if -r flag is present resolve the ec address from the dns name. 926 | if *res { 927 | _, e, err := factom.ResolveDnsName(toec) 928 | if err != nil { 929 | errorln(err) 930 | return 931 | } 932 | toec = e 933 | } 934 | 935 | var amt uint64 936 | if i, err := strconv.Atoi(args[2]); err != nil { 937 | errorln(err) 938 | return 939 | } else if i < 0 { 940 | errorln("AMOUNT may not be less than 0") 941 | return 942 | } else { 943 | rate, err := factom.GetECRate() 944 | if err != nil { 945 | errorln(err) 946 | } 947 | amt = uint64(i) * rate 948 | } 949 | 950 | tx, err := factom.BuyEC(args[0], toec, amt, *fflag) 951 | if err != nil { 952 | errorln(err) 953 | return 954 | } 955 | switch { 956 | case *qflag: 957 | case *tdisp: 958 | fmt.Println(tx.TxID) 959 | default: 960 | fmt.Println("TxID:", tx.TxID) 961 | } 962 | 963 | // wait for the transaction to be acknowledged by the server 964 | if !*fflag { 965 | s, err := waitOnFctAck(tx.TxID) 966 | if err != nil { 967 | errorln(err) 968 | return 969 | } 970 | if !*qflag && !*tdisp { 971 | fmt.Println("Status:", s) 972 | } 973 | } 974 | } 975 | help.Add("buyec", cmd) 976 | return cmd 977 | }() 978 | -------------------------------------------------------------------------------- /transaction_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | _ = fmt.Sprint("testing") 10 | ) 11 | 12 | func TestPrint(t *testing.T) { 13 | vs1 := []uint64{100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1} 14 | vs2 := []int64{100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1, 15 | -100000000, -10000000, -1000000, -100000, -10000, -1000, -100, -10, -1} 16 | ans1 := []string{"1", "0.1", "0.01", "0.001", "0.0001", "0.00001", 17 | "0.000001", "0.0000001", "0.00000001"} 18 | ans2 := []string{"1", "0.1", "0.01", "0.001", "0.0001", "0.00001", 19 | "0.000001", "0.0000001", "0.00000001", "-1", "-0.1", "-0.01", "-0.001", 20 | "-0.0001", "-0.00001", "-0.000001", "-0.0000001", "-0.00000001"} 21 | for i, v := range vs1 { 22 | a := factoshiToFactoid(v) 23 | if a != ans1[i] { 24 | t.Errorf("The value %d is %s not %s as expected", v, a, ans1[i]) 25 | } 26 | } 27 | for i, v := range vs2 { 28 | a := factoshiToFactoid(v) 29 | if a != ans2[i] { 30 | t.Errorf("The value %d is %s not %s as expected", v, a, ans2[i]) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /unlock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | "strconv" 12 | 13 | "github.com/FactomProject/factom" 14 | "github.com/posener/complete" 15 | ) 16 | 17 | // unlockwallet creates a new transaction in the wallet. 18 | var unlockwallet = func() *fctCmd { 19 | cmd := new(fctCmd) 20 | cmd.helpMsg = "factom-cli unlockwallet [-v] \"passphrase\" " 21 | cmd.description = "Unlock the wallet for some number of seconds; must be an encrypted wallet. -v verbose." 22 | cmd.completion = complete.Command{ 23 | Flags: complete.Flags{ 24 | "-v": complete.PredictNothing, 25 | }, 26 | } 27 | cmd.execFunc = func(args []string) { 28 | os.Args = args 29 | vflag := flag.Bool("v", false, "verbose mode; print relock time in UTC epoch seconds when the unlock is successful") 30 | flag.Parse() 31 | args = flag.Args() 32 | 33 | if len(args) != 2 { 34 | fmt.Println(cmd.helpMsg) 35 | return 36 | } 37 | 38 | seconds, err := strconv.ParseInt(args[1], 10, 64) 39 | if err != nil { 40 | errorln(err) 41 | return 42 | } 43 | 44 | unix, err := factom.UnlockWallet(args[0], seconds) 45 | if err != nil { 46 | errorln(err) 47 | return 48 | } 49 | 50 | // output 51 | switch { 52 | // verbose mode; print when wallet will re-lock 53 | case *vflag: 54 | fmt.Println(unix) 55 | default: 56 | } 57 | } 58 | help.Add("unlockwallet", cmd) 59 | return cmd 60 | }() 61 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "crypto/sha256" 9 | "encoding/hex" 10 | "fmt" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | "github.com/FactomProject/btcutil/base58" 16 | "github.com/FactomProject/factom" 17 | ) 18 | 19 | // GetFactomdServer returns the current factomd server set in the factom package. 20 | // This is used for compose functions to put the appropriate url in the sample curl 21 | // localhost:8088 is returned if the factom package does not have one set 22 | func GetFactomdServer() string { 23 | if factom.RpcConfig != nil && factom.RpcConfig.FactomdServer != "" { 24 | return factom.RpcConfig.FactomdServer 25 | } 26 | return "localhost:8088" 27 | } 28 | 29 | // exidCollector accumulates the external ids from the command line -e and -E 30 | // flags 31 | var exidCollector [][]byte 32 | 33 | // extids will be a flag receiver for adding chains and entries 34 | // In ASCII 35 | type extidsASCII []string 36 | 37 | func (e *extidsASCII) String() string { 38 | return fmt.Sprint(*e) 39 | } 40 | 41 | func (e *extidsASCII) Set(s string) error { 42 | *e = append(*e, s) 43 | exidCollector = append(exidCollector[:], []byte(s)) 44 | return nil 45 | } 46 | 47 | // extids will be a flag receiver for adding chains and entries 48 | // In HEX 49 | type extidsHex []string 50 | 51 | func (e *extidsHex) String() string { 52 | return fmt.Sprint(*e) 53 | } 54 | 55 | func (e *extidsHex) Set(s string) error { 56 | *e = append(*e, s) 57 | b, err := hex.DecodeString(s) 58 | if err != nil { 59 | return err 60 | } 61 | exidCollector = append(exidCollector[:], b) 62 | return nil 63 | } 64 | 65 | // nameCollector accumulates the components of a chain name from the command 66 | // line -n and -N flags 67 | var nameCollector [][]byte 68 | 69 | // namesASCII will be a flag receiver for ASCII chain names. 70 | type namesASCII []string 71 | 72 | func (n *namesASCII) String() string { 73 | return fmt.Sprint(*n) 74 | } 75 | 76 | func (n *namesASCII) Set(s string) error { 77 | *n = append(*n, s) 78 | nameCollector = append(nameCollector[:], []byte(s)) 79 | return nil 80 | } 81 | 82 | // namesHex will be a flag receiver for HEX encoded chain names. 83 | type namesHex []string 84 | 85 | func (n *namesHex) String() string { 86 | return fmt.Sprint(*n) 87 | } 88 | 89 | func (n *namesHex) Set(s string) error { 90 | *n = append(*n, s) 91 | b, err := hex.DecodeString(s) 92 | if err != nil { 93 | return err 94 | } 95 | nameCollector = append(nameCollector[:], b) 96 | return nil 97 | } 98 | 99 | // keysASCII will be a flag receiver for ASCII identity keys. 100 | type keysASCII []string 101 | 102 | func (n *keysASCII) String() string { 103 | return fmt.Sprint(*n) 104 | } 105 | 106 | func (k *keysASCII) Set(s string) error { 107 | *k = append(*k, s) 108 | if factom.IdentityKeyStringType(s) != factom.IDPub { 109 | return fmt.Errorf("Provided key string not a valid public identity key: %s", s) 110 | } 111 | b := base58.Decode(s) 112 | key := factom.NewIdentityKey() 113 | copy(key.Pub[:], b[factom.IDKeyPrefixLength:factom.IDKeyBodyLength]) 114 | return nil 115 | } 116 | 117 | func factoshiToFactoid(v interface{}) string { 118 | value, err := strconv.Atoi(fmt.Sprint(v)) 119 | if err != nil { 120 | return "" 121 | } 122 | sign := "" 123 | if value < 0 { 124 | sign = "-" 125 | value = -value 126 | } 127 | d := value / 1e8 128 | r := value % 1e8 129 | ds := fmt.Sprintf("%s%d", sign, d) 130 | rs := fmt.Sprintf("%08d", r) 131 | rs = strings.TrimRight(rs, "0") 132 | if len(rs) > 0 { 133 | ds = ds + "." 134 | } 135 | return fmt.Sprintf("%s%s", ds, rs) 136 | } 137 | 138 | // nametoid computes a chainid from the chain name components 139 | func nametoid(name [][]byte) string { 140 | hs := sha256.New() 141 | for _, v := range name { 142 | h := sha256.Sum256(v) 143 | hs.Write(h[:]) 144 | } 145 | return hex.EncodeToString(hs.Sum(nil)) 146 | } 147 | 148 | // waitOnFctAck blocks while waiting for a factom ack message and returns the 149 | // ack status or times out after 10 seconds. 150 | func waitOnFctAck(txid string) (string, error) { 151 | stat := make(chan string, 1) 152 | errchan := make(chan error, 1) 153 | 154 | // poll for the acknowledgement 155 | go func() { 156 | for { 157 | s, err := factom.FactoidACK(txid, "") 158 | if err != nil { 159 | errchan <- err 160 | break 161 | } 162 | if (s.Status != "Unknown") && (s.Status != "NotConfirmed") { 163 | stat <- s.Status 164 | break 165 | } 166 | time.Sleep(time.Second / 2) 167 | } 168 | }() 169 | 170 | // wait for the acknowledgement or timeout after 10 sec 171 | select { 172 | case err := <-errchan: 173 | return "", err 174 | case s := <-stat: 175 | return s, nil 176 | case <-time.After(60 * time.Second): 177 | return "", fmt.Errorf("timeout: no acknowledgement found") 178 | } 179 | 180 | // code should not reach this point 181 | return "", fmt.Errorf("unknown error") 182 | } 183 | 184 | // waitOnCommitAck blocks while waiting for an ack message for an Entry Commit 185 | // and returns the ack status or times out after 10 seconds. 186 | func waitOnCommitAck(txid string) (string, error) { 187 | stat := make(chan string, 1) 188 | errchan := make(chan error, 1) 189 | 190 | // poll for the acknowledgement 191 | go func() { 192 | for { 193 | s, err := factom.EntryCommitACK(txid, "") 194 | if err != nil { 195 | errchan <- err 196 | break 197 | } 198 | 199 | if (s.CommitData.Status != "Unknown") && (s.CommitData.Status != "NotConfirmed") { 200 | stat <- s.CommitData.Status 201 | break 202 | } 203 | time.Sleep(time.Second / 2) 204 | } 205 | }() 206 | 207 | // wait for the acknowledgement or timeout after 10 sec 208 | select { 209 | case err := <-errchan: 210 | return "", err 211 | case s := <-stat: 212 | return s, nil 213 | case <-time.After(60 * time.Second): 214 | return "", fmt.Errorf("timeout: no acknowledgement found") 215 | } 216 | 217 | // code should not reach this point 218 | return "", fmt.Errorf("unknown error") 219 | } 220 | 221 | // waitOnRevealAck blocks while waiting for an ack message for an Entry Reveal 222 | // and returns the ack status or times out after 10 seconds. 223 | func waitOnRevealAck(txid string) (string, error) { 224 | stat := make(chan string, 1) 225 | errchan := make(chan error, 1) 226 | 227 | // poll for the acknowledgement 228 | go func() { 229 | for { 230 | // All 0s signals an entry 231 | s, err := factom.EntryRevealACK(txid, "", "0000000000000000000000000000000000000000000000000000000000000000") 232 | if err != nil { 233 | errchan <- err 234 | break 235 | } 236 | 237 | if (s.EntryData.Status != "Unknown") && (s.EntryData.Status != "NotConfirmed") { 238 | stat <- s.EntryData.Status 239 | break 240 | } 241 | time.Sleep(time.Second / 2) 242 | } 243 | }() 244 | 245 | // wait for the acknowledgement or timeout after 10 sec 246 | select { 247 | case err := <-errchan: 248 | return "", err 249 | case s := <-stat: 250 | return s, nil 251 | case <-time.After(60 * time.Second): 252 | return "", fmt.Errorf("timeout: no acknowledgement found") 253 | } 254 | 255 | // code should not reach this point 256 | return "", fmt.Errorf("unknown error") 257 | } 258 | -------------------------------------------------------------------------------- /util_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestGetFactomdServer(t *testing.T) { 6 | tests := []struct { 7 | name string 8 | want string 9 | }{ 10 | 11 | {name: "test", want: "localhost:8088"}, 12 | } 13 | 14 | for _, tt := range tests { 15 | t.Run(tt.name, func(t *testing.T) { 16 | if got := GetFactomdServer(); got != tt.want { 17 | t.Errorf("GetFactomdServer() = %v, want %v", got, tt.want) 18 | } 19 | }) 20 | } 21 | } 22 | 23 | func Test_extidsHex_Set(t *testing.T) { 24 | type args struct { 25 | s string 26 | } 27 | tests := []struct { 28 | name string 29 | e *extidsHex 30 | args args 31 | wantErr bool 32 | }{ 33 | // TODO: Add test cases. 34 | } 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | if err := tt.e.Set(tt.args.s); (err != nil) != tt.wantErr { 38 | t.Errorf("extidsHex.Set() error = %v, wantErr %v", err, tt.wantErr) 39 | } 40 | }) 41 | } 42 | } 43 | 44 | func Test_namesASCII_Set(t *testing.T) { 45 | type args struct { 46 | s string 47 | } 48 | tests := []struct { 49 | name string 50 | n *namesASCII 51 | args args 52 | wantErr bool 53 | }{ 54 | // TODO: Add test cases. 55 | } 56 | for _, tt := range tests { 57 | t.Run(tt.name, func(t *testing.T) { 58 | if err := tt.n.Set(tt.args.s); (err != nil) != tt.wantErr { 59 | t.Errorf("namesASCII.Set() error = %v, wantErr %v", err, tt.wantErr) 60 | } 61 | }) 62 | } 63 | } 64 | 65 | func Test_namesHex_Set(t *testing.T) { 66 | type args struct { 67 | s string 68 | } 69 | tests := []struct { 70 | name string 71 | n *namesHex 72 | args args 73 | wantErr bool 74 | }{ 75 | // TODO: Add test cases. 76 | } 77 | for _, tt := range tests { 78 | t.Run(tt.name, func(t *testing.T) { 79 | if err := tt.n.Set(tt.args.s); (err != nil) != tt.wantErr { 80 | t.Errorf("namesHex.Set() error = %v, wantErr %v", err, tt.wantErr) 81 | } 82 | }) 83 | } 84 | } 85 | 86 | func Test_extidsASCII_String(t *testing.T) { 87 | tests := []struct { 88 | name string 89 | e *extidsASCII 90 | want string 91 | }{ 92 | // TODO: Add test cases. 93 | } 94 | for _, tt := range tests { 95 | t.Run(tt.name, func(t *testing.T) { 96 | if got := tt.e.String(); got != tt.want { 97 | t.Errorf("extidsASCII.String() = %v, want %v", got, tt.want) 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func Test_extidsHex_String(t *testing.T) { 104 | tests := []struct { 105 | name string 106 | e *extidsHex 107 | want string 108 | }{ 109 | // TODO: Add test cases. 110 | } 111 | for _, tt := range tests { 112 | t.Run(tt.name, func(t *testing.T) { 113 | if got := tt.e.String(); got != tt.want { 114 | t.Errorf("extidsHex.String() = %v, want %v", got, tt.want) 115 | } 116 | }) 117 | } 118 | } 119 | 120 | func Test_namesASCII_String(t *testing.T) { 121 | tests := []struct { 122 | name string 123 | n *namesASCII 124 | want string 125 | }{ 126 | // TODO: Add test cases. 127 | } 128 | for _, tt := range tests { 129 | t.Run(tt.name, func(t *testing.T) { 130 | if got := tt.n.String(); got != tt.want { 131 | t.Errorf("namesASCII.String() = %v, want %v", got, tt.want) 132 | } 133 | }) 134 | } 135 | } 136 | 137 | func Test_namesHex_String(t *testing.T) { 138 | tests := []struct { 139 | name string 140 | n *namesHex 141 | want string 142 | }{ 143 | // TODO: Add test cases. 144 | } 145 | for _, tt := range tests { 146 | t.Run(tt.name, func(t *testing.T) { 147 | if got := tt.n.String(); got != tt.want { 148 | t.Errorf("namesHex.String() = %v, want %v", got, tt.want) 149 | } 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /walletbackup.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Factom Foundation 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | 12 | "github.com/FactomProject/factom" 13 | "github.com/posener/complete" 14 | ) 15 | 16 | // backupwallet returns the wallet seed and all of the addresses from the 17 | // wallet. 18 | var backupwallet = func() *fctCmd { 19 | cmd := new(fctCmd) 20 | cmd.helpMsg = "factom-cli backupwallet" 21 | cmd.description = "Backup the running wallet" 22 | cmd.completion = complete.Command{} 23 | cmd.execFunc = func(args []string) { 24 | os.Args = args 25 | flag.Parse() 26 | args = flag.Args() 27 | 28 | s, err := factom.BackupWallet() 29 | if err != nil { 30 | errorln(err) 31 | return 32 | } 33 | fmt.Print(s) 34 | } 35 | help.Add("backupwallet", cmd) 36 | return cmd 37 | }() 38 | --------------------------------------------------------------------------------