├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── account ├── account.go ├── account_test.go ├── accounts.go └── accounts_test.go ├── buildlibsecp256k1.sh ├── did ├── did.go └── did_test.go ├── errcodes └── errcodes.go ├── examples ├── chaininfo │ ├── main.go │ └── service.go ├── openoracle │ ├── OpenOraclePriceData.abi │ ├── OpenOraclePriceData.bin │ ├── README.md │ ├── main.go │ └── service.go ├── util │ └── service.go └── xrc20tokens │ ├── README.md │ ├── XRC20.abi │ ├── XRC20.bin │ ├── main.go │ ├── service.go │ ├── xrc20.sol │ └── xrc20tokens ├── go.mod ├── go.sum ├── iotex ├── caller_claimreward.go ├── caller_contract.go ├── caller_staking.go ├── caller_staking_test.go ├── caller_transfer.go ├── callers.go ├── client.go ├── contract.go ├── default.go ├── interfaces.go ├── interfaces_mock.go ├── iotex_test.go ├── utils.go └── utils_rlp.go ├── jwt ├── jwt.go └── jwt_test.go ├── utils ├── unit │ ├── unit.go │ └── unit_test.go └── wait │ └── wait.go └── version └── version.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 1.x 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: ^1.17 20 | 21 | - name: Check out code into the Go module directory 22 | uses: actions/checkout@v2 23 | 24 | - name: Get dependencies 25 | run: | 26 | go get -v -t -d ./... 27 | if [ -f Gopkg.toml ]; then 28 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 29 | dep ensure 30 | fi 31 | 32 | - name: Build 33 | run: go build -v ./... 34 | 35 | - name: Test 36 | run: go test -v -short -race ./... 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | *.db 4 | 5 | .cache 6 | 7 | *.DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # profiling output 12 | *prof* 13 | 14 | # Binaries for programs and plugins 15 | *.exe 16 | *.dll 17 | *.dylib 18 | *.pyc 19 | 20 | # Test binary, build with `go test -c` 21 | *.test 22 | 23 | #git patch 24 | *.patch 25 | 26 | # Output of the go coverage tool, specifically when used with LiteIDE 27 | *.out 28 | 29 | # wallet date file 30 | *.dat 31 | 32 | # binary 33 | bin/* 34 | coverage.html 35 | lint.log 36 | .editorconfig 37 | coverage.txt 38 | 39 | vendor/ 40 | .vscode 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ######################################################################################################################## 2 | # Copyright (c) 2019 IoTeX 3 | # This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 4 | # warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 5 | # permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 6 | # License 2.0 that can be found in the LICENSE file. 7 | ######################################################################################################################## 8 | 9 | # Go parameters 10 | GOCMD=go 11 | GOLINT=golint -set_exit_status 12 | GOBUILD=$(GOCMD) build 13 | GOCLEAN=$(GOCMD) clean 14 | GOTEST=$(GOCMD) test 15 | BUILD_TARGET_SERVER=antenna-go 16 | 17 | # Pkgs 18 | ALL_PKGS := $(shell go list ./... ) 19 | PKGS := $(shell go list ./... | grep -v /vendor/ ) 20 | ROOT_PKG := "github.com/iotexproject/iotex-antenna-go" 21 | 22 | # Docker parameters 23 | DOCKERCMD=docker 24 | 25 | # Package Info 26 | PACKAGE_VERSION := $(shell git describe --tags --always) 27 | PACKAGE_COMMIT_ID := $(shell git rev-parse HEAD) 28 | GIT_STATUS := $(shell git status --porcelain) 29 | ifdef GIT_STATUS 30 | GIT_STATUS := "dirty" 31 | else 32 | GIT_STATUS := "clean" 33 | endif 34 | GO_VERSION := $(shell go version) 35 | BUILD_TIME=$(shell date +%F-%Z/%T) 36 | VersionImportPath := github.com/iotexproject/iotex-antenna/version 37 | PackageFlags += -X '$(VersionImportPath).PackageVersion=$(PACKAGE_VERSION)' 38 | PackageFlags += -X '$(VersionImportPath).PackageCommitID=$(PACKAGE_COMMIT_ID)' 39 | PackageFlags += -X '$(VersionImportPath).GitStatus=$(GIT_STATUS)' 40 | PackageFlags += -X '$(VersionImportPath).GoVersion=$(GO_VERSION)' 41 | PackageFlags += -X '$(VersionImportPath).BuildTime=$(BUILD_TIME)' 42 | PackageFlags += -s -w 43 | 44 | V ?= 0 45 | ifeq ($(V),0) 46 | ECHO_V = @ 47 | else 48 | VERBOSITY_FLAG = -v 49 | DEBUG_FLAG = -debug 50 | endif 51 | 52 | all: test build clean 53 | 54 | .PHONY: build 55 | build: 56 | $(GOBUILD) -ldflags "$(PackageFlags)" -v ./... 57 | 58 | .PHONY: fmt 59 | fmt: 60 | $(GOCMD) fmt ./... 61 | 62 | .PHONY: test 63 | test: lint fmt 64 | $(GOTEST) ./... -v -short -race 65 | 66 | .PHONY: lint 67 | lint: 68 | go list ./... | grep -v /vendor/ | xargs $(GOLINT) 69 | 70 | .PHONY: mockgen 71 | mockgen: 72 | mockgen -destination=./iotex/interfaces_mock.go -source=./iotex/interfaces.go -package=iotex 73 | 74 | .PHONY: examples 75 | examples: 76 | $(GOBUILD) -o ./examples/chaininfo/chaininfo ./examples/chaininfo 77 | $(GOBUILD) -o ./examples/openoracle/openoracle ./examples/openoracle 78 | $(GOBUILD) -o ./examples/xrc20tokens/xrc20tokens ./examples/xrc20tokens 79 | 80 | .PHONY: clean 81 | clean: 82 | @echo "Cleaning..." 83 | $(ECHO_V)rm -rf ./$(BUILD_TARGET_SERVER) 84 | $(ECHO_V)$(GOCLEAN) -i $(PKGS) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iotex-antenna-go 2 | 3 | [![CircleCI](https://circleci.com/gh/iotexproject/iotex-antenna-go.svg?style=svg)](https://circleci.com/gh/iotexproject/iotex-antenna-go) 4 | [![Go version](https://img.shields.io/badge/go-1.11.5-blue.svg)](https://github.com/moovweb/gvm) 5 | [![LICENSE](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) 6 | 7 | This is the the official Go implementation of IoTeX SDK! Please refer to IoTeX [whitepaper](https://iotex.io/research) and the [protocol](https://github.com/iotexproject/iotex-core) for details. 8 | 9 | ## Get Started 10 | 11 | ### Minimum Requirements 12 | 13 | | Components | Version | Description | 14 | |----------|-------------|-------------| 15 | | [Golang](https://golang.org) | ≥ 1.11.5 | Go programming language | 16 | 17 | ### Add Dependency 18 | 19 | ``` 20 | // go mod 21 | go get github.com/iotexproject/iotex-antenna-go/v2 22 | ``` 23 | 24 | ### Code It Up 25 | The below example code shows the 4 easy steps to send a transaction to IoTeX blockchain 26 | 1. connect to the chain's RPC endpoint 27 | 2. create an account by importing a private key 28 | 3. create a client and generate an action sender 29 | 4. send the transaction to the chain 30 | 31 | ``` 32 | package main 33 | 34 | import ( 35 | "context" 36 | "fmt" 37 | "log" 38 | 39 | "github.com/iotexproject/iotex-address/address" 40 | "github.com/iotexproject/iotex-antenna-go/v2/account" 41 | "github.com/iotexproject/iotex-antenna-go/v2/iotex" 42 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 43 | ) 44 | 45 | const ( 46 | mainnetRPC = "api.iotex.one:443" 47 | testnetRPC = "api.testnet.iotex.one:443" 48 | mainnetChainID = 1 49 | testnetChainID = 2 50 | ) 51 | 52 | func main() { 53 | // Create grpc connection 54 | conn, err := iotex.NewDefaultGRPCConn(testnetRPC) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | defer conn.Close() 59 | 60 | // Add account by private key 61 | acc, err := account.HexStringToAccount("...") 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | 66 | // create client 67 | c := iotex.NewAuthedClient(iotexapi.NewAPIServiceClient(conn), testnetChainID, acc) 68 | 69 | // send the transfer to chain 70 | to, err := address.FromString("io1zq5g9c5c3hqw9559ks4anptkpumxgsjfn2e4ke") 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | hash, err := c.Transfer(to, big.NewInt(10)).SetGasPrice(big.NewInt(100000000000)).SetGasLimit(20000).Call(context.Background()) 75 | if err != nil { 76 | log.Fatal(err) 77 | } 78 | fmt.Println("transaction hash = %x\n", hash) 79 | } 80 | ``` 81 | 82 | ### More Examples 83 | There are three examples demostrating the use of this SDK on Testnet. You can `make examples` to build and try: 84 | - `./examples/chaininfo` shows **how to use the SDK to pull chain, block, action and delegates info** 85 | - `./examples/openoracle` shows **how to deploy and invoke [Open Oracle Contracts](https://github.com/compound-finance/open-oracle)** 86 | - `./examples/xrc20tokens` shows **how to deploy and invoke XRC20 tokens** 87 | -------------------------------------------------------------------------------- /account/account.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package account 8 | 9 | import ( 10 | "fmt" 11 | 12 | ethCrypto "github.com/ethereum/go-ethereum/crypto" 13 | 14 | "github.com/iotexproject/go-pkgs/crypto" 15 | "github.com/iotexproject/go-pkgs/hash" 16 | "github.com/iotexproject/iotex-address/address" 17 | ) 18 | 19 | type ( 20 | // Account is a user account 21 | Account interface { 22 | // Address returns the IoTeX address 23 | Address() address.Address 24 | // PrivateKey returns the embedded private key interface 25 | PrivateKey() crypto.PrivateKey 26 | // PublicKey returns the embedded public key interface 27 | PublicKey() crypto.PublicKey 28 | // Sign signs the message using the private key 29 | Sign([]byte) ([]byte, error) 30 | // Verify verifies the message using the public key 31 | Verify([]byte, []byte) bool 32 | // Zero zeroes the private key data 33 | Zero() 34 | // SignMessage signs the message using preamble 35 | SignMessage(data []byte) ([]byte, error) 36 | } 37 | 38 | account struct { 39 | private crypto.PrivateKey 40 | address address.Address 41 | } 42 | ) 43 | 44 | // NewAccount generates a new account 45 | func NewAccount() (Account, error) { 46 | pk, err := crypto.GenerateKey() 47 | if err != nil { 48 | return nil, err 49 | } 50 | addr, err := address.FromBytes(pk.PublicKey().Hash()) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return &account{ 55 | pk, 56 | addr, 57 | }, nil 58 | } 59 | 60 | // HexStringToAccount generates an account from private key string 61 | func HexStringToAccount(privateKey string) (Account, error) { 62 | sk, err := crypto.HexStringToPrivateKey(privateKey) 63 | if err != nil { 64 | return nil, err 65 | } 66 | addr, err := address.FromBytes(sk.PublicKey().Hash()) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return &account{ 71 | sk, 72 | addr, 73 | }, nil 74 | } 75 | 76 | // PrivateKeyToAccount generates an account from an existing private key interface 77 | func PrivateKeyToAccount(key crypto.PrivateKey) (Account, error) { 78 | addr, err := address.FromBytes(key.PublicKey().Hash()) 79 | if err != nil { 80 | return nil, err 81 | } 82 | return &account{ 83 | key, 84 | addr, 85 | }, nil 86 | } 87 | 88 | // Address returns the IoTeX address 89 | func (act *account) Address() address.Address { 90 | return act.address 91 | } 92 | 93 | // PrivateKey return the embedded private key 94 | func (act *account) PrivateKey() crypto.PrivateKey { 95 | return act.private 96 | } 97 | 98 | // PublicKey returns the embedded public key interface 99 | func (act *account) PublicKey() crypto.PublicKey { 100 | return act.private.PublicKey() 101 | } 102 | 103 | // Sign signs the message using the private key 104 | func (act *account) Sign(data []byte) ([]byte, error) { 105 | h := hash.Hash256b(data) 106 | return act.private.Sign(h[:]) 107 | } 108 | 109 | // Verify verifies the message using the public key 110 | func (act *account) Verify(data []byte, sig []byte) bool { 111 | h := hash.Hash256b(data) 112 | return act.PublicKey().Verify(h[:], sig) 113 | } 114 | 115 | // Zero zeroes the private key data 116 | func (act *account) Zero() { 117 | act.private.Zero() 118 | } 119 | 120 | // SignMessage signs the message using preamble 121 | func (act *account) SignMessage(data []byte) ([]byte, error) { 122 | h := HashMessage(data) 123 | return act.private.Sign(h[:]) 124 | } 125 | 126 | // HashMessage hash the message using preamble 127 | func HashMessage(data []byte) hash.Hash256 { 128 | preamble := fmt.Sprintf("\x16IoTeX Signed Message:\n%d", len(data)) 129 | iotexMessage := []byte(preamble) 130 | iotexMessage = append(iotexMessage, data...) 131 | return hash.Hash256b(iotexMessage) 132 | } 133 | 134 | // RecoverAddress recover address by message hash and signature 135 | func RecoverAddress(messageHash, signature []byte) (address.Address, error) { 136 | if len(signature) != 65 { 137 | return nil, fmt.Errorf("wrong size for signature: got %d, want 65", len(signature)) 138 | } 139 | 140 | pub, err := ethCrypto.Ecrecover(messageHash, signature) 141 | if err != nil { 142 | return nil, err 143 | } 144 | 145 | payload := hash.Hash160b(pub[1:]) 146 | return address.FromBytes(payload[:]) 147 | } 148 | -------------------------------------------------------------------------------- /account/account_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package account 8 | 9 | import ( 10 | "encoding/hex" 11 | "testing" 12 | 13 | "github.com/iotexproject/go-pkgs/hash" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | var ( 18 | Address = "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j" 19 | PrivateKey = "0806c458b262edd333a191e92f561aff338211ee3e18ab315a074a2d82aa343f" 20 | PublicKey = "044e18306ae9ef4ec9d07bf6e705442d4d1a75e6cdf750330ca2d880f2cc54607c9c33deb9eae9c06e06e04fe9ce3d43962cc67d5aa34fbeb71270d4bad3d648d9" 21 | ) 22 | 23 | const text = "IoTeX is the auto-scalable and privacy-centric blockchain." 24 | 25 | func TestHash160b(t *testing.T) { 26 | h := hash.Hash160b([]byte(text)) 27 | assert.Equal(t, "93988dc3d2d879f703c7d3f54dcc1b473b27d015", hex.EncodeToString(h[:])) 28 | } 29 | 30 | func TestHash256b(t *testing.T) { 31 | h := hash.Hash256b([]byte(text)) 32 | assert.Equal(t, "aada23f93a5ed1829ebf1c0693988dc3d2d879f703c7d3f54dcc1b473b27d015", hex.EncodeToString(h[:])) 33 | } 34 | 35 | func TestAccount(t *testing.T) { 36 | assert := assert.New(t) 37 | 38 | act, err := HexStringToAccount(PrivateKey) 39 | assert.NoError(err) 40 | assert.Equal(Address, act.Address().String()) 41 | assert.Equal(PublicKey, act.PrivateKey().PublicKey().HexString()) 42 | 43 | act1, err := PrivateKeyToAccount(act.PrivateKey()) 44 | assert.NoError(err) 45 | assert.Equal(act, act1) 46 | 47 | b, err := act.Sign([]byte(text)) 48 | assert.NoError(err) 49 | assert.Equal( 50 | "482da72c8faa48ee1ac2cf9a5f9ecd42ee3258be5ddd8d6b496c7171dc7bfe8e75e5d16e7129c88d99a21a912e5c082fa1baab6ba87d2688ebd7d27bb1ab090701", 51 | hex.EncodeToString(b), 52 | ) 53 | // verify the signature 54 | assert.True(act.Verify([]byte(text), b)) 55 | 56 | act.Zero() 57 | b, err = act.Sign([]byte(text)) 58 | assert.Equal("invalid private key", err.Error()) 59 | } 60 | 61 | func TestHashMessage(t *testing.T) { 62 | assert := assert.New(t) 63 | 64 | h := HashMessage([]byte("hello")) 65 | assert.Equal( 66 | "5077b388a631936d73d9c6c9a0bf6016843a8b594540d1d968f7ea40d1541c58", 67 | hex.EncodeToString(h[:]), 68 | ) 69 | } 70 | 71 | func TestSignMessage(t *testing.T) { 72 | assert := assert.New(t) 73 | 74 | act, err := HexStringToAccount(PrivateKey) 75 | assert.NoError(err) 76 | 77 | b, err := act.SignMessage([]byte("hello")) 78 | assert.NoError(err) 79 | assert.Equal( 80 | "f09c729cc8617aeda344defba6c0eb0eb3ee71732e26f22d1a9fac5beeaa86da3a368417e31779b44e3df4440dfec89a9ecb40567b60228efb67c79672288cef01", 81 | hex.EncodeToString(b), 82 | ) 83 | } 84 | 85 | func TestRecover(t *testing.T) { 86 | assert := assert.New(t) 87 | 88 | h, _ := hex.DecodeString("5077b388a631936d73d9c6c9a0bf6016843a8b594540d1d968f7ea40d1541c58") 89 | sig, _ := hex.DecodeString("f09c729cc8617aeda344defba6c0eb0eb3ee71732e26f22d1a9fac5beeaa86da3a368417e31779b44e3df4440dfec89a9ecb40567b60228efb67c79672288cef01") 90 | 91 | addr, err := RecoverAddress(h, sig) 92 | assert.NoError(err) 93 | assert.Equal(Address, addr.String()) 94 | } 95 | -------------------------------------------------------------------------------- /account/accounts.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package account 8 | 9 | import ( 10 | "github.com/iotexproject/iotex-address/address" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | // Accounts type 15 | type Accounts struct { 16 | accounts map[string]Account 17 | } 18 | 19 | // NewAccounts return Accounts instance 20 | func NewAccounts() *Accounts { 21 | accounts := make(map[string]Account) 22 | return &Accounts{ 23 | accounts: accounts, 24 | } 25 | } 26 | 27 | // Create new account 28 | func (acts *Accounts) Create() (Account, error) { 29 | acc, err := NewAccount() 30 | if err != nil { 31 | return nil, err 32 | } 33 | acts.accounts[acc.Address().String()] = acc 34 | return acc, nil 35 | } 36 | 37 | // GetAccount by address 38 | func (acts *Accounts) GetAccount(addr address.Address) (Account, error) { 39 | acc, ok := acts.accounts[addr.String()] 40 | if !ok { 41 | return nil, errors.Errorf("Account %s does not exist", addr) 42 | } 43 | return acc, nil 44 | } 45 | 46 | // AddAccount add an account 47 | func (acts *Accounts) AddAccount(acc Account) error { 48 | addr := acc.Address() 49 | if _, ok := acts.accounts[addr.String()]; ok { 50 | return errors.Errorf("Account %s already exists", addr) 51 | } 52 | acts.accounts[addr.String()] = acc 53 | return nil 54 | } 55 | 56 | // RemoveAccount removes an account 57 | func (acts *Accounts) RemoveAccount(addr address.Address) { 58 | if v, ok := acts.accounts[addr.String()]; ok { 59 | // zero the private key 60 | v.Zero() 61 | } 62 | delete(acts.accounts, addr.String()) 63 | } 64 | -------------------------------------------------------------------------------- /account/accounts_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package account 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestAccounts_Create(t *testing.T) { 16 | assert := assert.New(t) 17 | 18 | acts := NewAccounts() 19 | act, err := acts.Create() 20 | assert.NoError(err) 21 | assert.NotEmpty(act.PrivateKey()) 22 | assert.NotEmpty(act.PublicKey()) 23 | assert.NotEmpty(act.Address) 24 | 25 | b, err := acts.GetAccount(act.Address()) 26 | assert.NoError(err) 27 | assert.Equal(act, b) 28 | } 29 | 30 | func TestAccounts_AddAccount(t *testing.T) { 31 | assert := assert.New(t) 32 | 33 | acts := NewAccounts() 34 | act, err := HexStringToAccount(PrivateKey) 35 | assert.NoError(err) 36 | assert.NoError(acts.AddAccount(act)) 37 | 38 | b, err := acts.GetAccount(act.Address()) 39 | assert.NoError(err) 40 | assert.Equal(act, b) 41 | 42 | acts.RemoveAccount(act.Address()) 43 | b, err = acts.GetAccount(act.Address()) 44 | assert.Error(err) 45 | assert.Nil(b) 46 | } 47 | -------------------------------------------------------------------------------- /buildlibsecp256k1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | apt-get update 3 | sudo apt-get install openssl -y 4 | sudo apt-get install libssl-dev -y 5 | sudo apt-get install autoconf -y 6 | sudo apt-get install default-jre -y 7 | sudo apt-get install build-essential automake libtool pkg-config libffi-dev python-dev python-pip -y 8 | cd vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1 9 | ./autogen.sh 10 | ./configure --disable-shared --with-pic --with-bignum=no --enable-module-recovery --disable-jni 11 | make 12 | sudo make install -------------------------------------------------------------------------------- /did/did.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package did 8 | 9 | import ( 10 | "encoding/hex" 11 | 12 | "github.com/iotexproject/go-pkgs/crypto" 13 | ) 14 | 15 | const ( 16 | // DIDContext is the general context 17 | DIDContext = "https://www.w3.org/ns/did/v1" 18 | // DIDPrefix is the prefix string 19 | DIDPrefix = "did:io:" 20 | // DIDAuthType is the authentication type 21 | DIDAuthType = "EcdsaSecp256k1VerificationKey2019" 22 | // DIDOwner is the suffix string 23 | DIDOwner = "#owner" 24 | ) 25 | 26 | type ( 27 | authentication struct { 28 | ID string `json:"id,omitempty"` 29 | Type string `json:"type,omitempty"` 30 | Controller string `json:"controller,omitempty"` 31 | PublicKeyHex string `json:"publicKeyHex,omitempty"` 32 | } 33 | // Doc is the DID document struct 34 | Doc struct { 35 | Context string `json:"@context,omitempty"` 36 | ID string `json:"id,omitempty"` 37 | Authentication []authentication `json:"authentication,omitempty"` 38 | } 39 | ) 40 | 41 | // CreateDID creates a new DID using public key 42 | func CreateDID(pk crypto.PublicKey) Doc { 43 | id := DIDPrefix + "0x" + hex.EncodeToString(pk.Hash()) 44 | return Doc{ 45 | Context: DIDContext, 46 | ID: id, 47 | Authentication: []authentication{ 48 | authentication{ 49 | ID: id + DIDOwner, 50 | Type: DIDAuthType, 51 | Controller: id, 52 | PublicKeyHex: pk.HexString(), 53 | }, 54 | }, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /did/did_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package did 8 | 9 | import ( 10 | "encoding/hex" 11 | "testing" 12 | 13 | "github.com/iotexproject/go-pkgs/crypto" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestCreateDID(t *testing.T) { 18 | r := require.New(t) 19 | 20 | a, err := crypto.GenerateKey() 21 | r.NoError(err) 22 | id := DIDPrefix + "0x" + hex.EncodeToString(a.PublicKey().Hash()) 23 | 24 | d := CreateDID(a.PublicKey()) 25 | r.Equal(id, d.ID) 26 | r.Equal(1, len(d.Authentication)) 27 | r.Equal(id+DIDOwner, d.Authentication[0].ID) 28 | r.Equal(DIDAuthType, d.Authentication[0].Type) 29 | r.Equal(id, d.Authentication[0].Controller) 30 | r.Equal(a.PublicKey().HexString(), d.Authentication[0].PublicKeyHex) 31 | } 32 | -------------------------------------------------------------------------------- /errcodes/errcodes.go: -------------------------------------------------------------------------------- 1 | package errcodes 2 | 3 | import "errors" 4 | 5 | // Code is a error code 6 | type Code int 7 | 8 | // Codes 9 | const ( 10 | InvalidParam Code = iota + 1 11 | RPCError 12 | BadResponse 13 | InternalError 14 | ) 15 | 16 | // ErrorWithCode is an error with an associated code. 17 | type ErrorWithCode interface { 18 | Error() string 19 | Code() Code 20 | Cause() error 21 | } 22 | 23 | type ewc struct { 24 | err error 25 | code Code 26 | } 27 | 28 | func (e *ewc) Error() string { return e.err.Error() } 29 | func (e *ewc) Code() Code { return e.code } 30 | func (e *ewc) Cause() error { return e.err } 31 | 32 | // NewError takes an error and a code return a error associated with the code. 33 | func NewError(err error, c Code) error { 34 | return &ewc{ 35 | err: err, 36 | code: c, 37 | } 38 | } 39 | 40 | // New takes an error message string and a code return a error associated with the code. 41 | func New(msg string, c Code) error { 42 | return &ewc{ 43 | err: errors.New(msg), 44 | code: c, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/chaininfo/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package main 8 | 9 | import ( 10 | "context" 11 | "encoding/json" 12 | "fmt" 13 | 14 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 15 | ) 16 | 17 | func main() { 18 | s := NewGetInfoService("", "api.testnet.iotex.one:443", true) 19 | 20 | r, err := s.GetChainMeta(context.Background(), &iotexapi.GetChainMetaRequest{}) 21 | out, _ := json.MarshalIndent(r, "", "\t") 22 | fmt.Println("chain meta", string(out), err) 23 | 24 | blockMetasRequest := &iotexapi.GetBlockMetasRequest{ 25 | Lookup: &iotexapi.GetBlockMetasRequest_ByIndex{ 26 | ByIndex: &iotexapi.GetBlockMetasByIndexRequest{ 27 | Start: 10000, 28 | Count: 1, 29 | }, 30 | }, 31 | } 32 | BlockMetasResponse, err := s.GetBlockMetas(context.Background(), blockMetasRequest) 33 | out, _ = json.MarshalIndent(BlockMetasResponse, "", "\t") 34 | fmt.Println("block metas", string(out), err) 35 | 36 | getActionsRequest := &iotexapi.GetActionsRequest{ 37 | Lookup: &iotexapi.GetActionsRequest_ByIndex{ 38 | ByIndex: &iotexapi.GetActionsByIndexRequest{ 39 | Start: 1000000, 40 | Count: 1, 41 | }, 42 | }, 43 | } 44 | getActionsResponse, err := s.GetActions(context.Background(), getActionsRequest) 45 | out, _ = json.MarshalIndent(getActionsResponse, "", "\t") 46 | fmt.Println("action", string(out), err) 47 | 48 | getCandidatesResponse, err := s.GetStakingCandidates(context.Background(), 7060000) 49 | out, _ = json.MarshalIndent(getCandidatesResponse, "", "\t") 50 | fmt.Println("candidates", string(out), err) 51 | 52 | getBucketsResponse, err := s.GetStakingBuckets(context.Background(), 7060000) 53 | out, _ = json.MarshalIndent(getBucketsResponse, "", "\t") 54 | fmt.Println("buckets", string(out), err) 55 | } 56 | -------------------------------------------------------------------------------- /examples/chaininfo/service.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package main 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | 13 | "github.com/golang/protobuf/proto" 14 | "github.com/pkg/errors" 15 | 16 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 17 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 18 | 19 | "github.com/iotexproject/iotex-antenna-go/v2/examples/util" 20 | ) 21 | 22 | const ( 23 | protocolID = "staking" 24 | readBucketsLimit = 30000 25 | readCandidatesLimit = 20000 26 | ) 27 | 28 | // GetInfoService is the GetInfoService interface 29 | type GetInfoService interface { 30 | // GetChainMeta is the GetChainMeta interface 31 | GetChainMeta(ctx context.Context, in *iotexapi.GetChainMetaRequest) (*iotexapi.GetChainMetaResponse, error) 32 | // GetBlockMetas is the GetBlockMetas interface 33 | GetBlockMetas(ctx context.Context, in *iotexapi.GetBlockMetasRequest) (*iotexapi.GetBlockMetasResponse, error) 34 | // GetActions is the GetActions interface 35 | GetActions(ctx context.Context, in *iotexapi.GetActionsRequest) (*iotexapi.GetActionsResponse, error) 36 | // GetStakingBuckets is the GetStakingBuckets interface 37 | GetStakingBuckets(ctx context.Context, height uint64) (*iotextypes.VoteBucketList, error) 38 | // GetStakingCandidates is the GetStakingCandidates interface 39 | GetStakingCandidates(ctx context.Context, height uint64) (*iotextypes.CandidateListV2, error) 40 | } 41 | 42 | type getInfoService struct { 43 | util.IotexService 44 | } 45 | 46 | // NewGetInfoService returns GetInfoService 47 | func NewGetInfoService(accountPrivate, endpoint string, secure bool) GetInfoService { 48 | return &getInfoService{ 49 | util.NewIotexService(accountPrivate, endpoint, secure), 50 | } 51 | } 52 | 53 | // GetChainMeta is the GetChainMeta interface 54 | func (s *getInfoService) GetChainMeta(ctx context.Context, in *iotexapi.GetChainMetaRequest) (*iotexapi.GetChainMetaResponse, error) { 55 | err := s.Connect() 56 | if err != nil { 57 | return nil, err 58 | } 59 | return s.ReadOnlyClient().API().GetChainMeta(ctx, in) 60 | } 61 | 62 | // GetBlockMetas is the GetBlockMetas interface 63 | func (s *getInfoService) GetBlockMetas(ctx context.Context, in *iotexapi.GetBlockMetasRequest) (*iotexapi.GetBlockMetasResponse, error) { 64 | err := s.Connect() 65 | if err != nil { 66 | return nil, err 67 | } 68 | return s.ReadOnlyClient().API().GetBlockMetas(ctx, in) 69 | } 70 | 71 | // GetActions is the GetActions interface 72 | func (s *getInfoService) GetActions(ctx context.Context, in *iotexapi.GetActionsRequest) (*iotexapi.GetActionsResponse, error) { 73 | err := s.Connect() 74 | if err != nil { 75 | return nil, err 76 | } 77 | return s.ReadOnlyClient().API().GetActions(ctx, in) 78 | } 79 | 80 | // GetStakingBuckets is the GetStakingBuckets interface 81 | func (s *getInfoService) GetStakingBuckets(ctx context.Context, height uint64) (voteBucketListAll *iotextypes.VoteBucketList, err error) { 82 | err = s.Connect() 83 | if err != nil { 84 | return nil, err 85 | } 86 | voteBucketListAll = &iotextypes.VoteBucketList{} 87 | for i := uint32(0); ; i++ { 88 | offset := i * readBucketsLimit 89 | size := uint32(readBucketsLimit) 90 | voteBucketList, err := s.getStakingBuckets(ctx, offset, size, height) 91 | if err != nil { 92 | return nil, errors.Wrap(err, "failed to get bucket") 93 | } 94 | voteBucketListAll.Buckets = append(voteBucketListAll.Buckets, voteBucketList.Buckets...) 95 | if len(voteBucketList.Buckets) < readBucketsLimit { 96 | break 97 | } 98 | } 99 | return 100 | } 101 | 102 | // GetStakingCandidates is the GetStakingCandidates interface 103 | func (s *getInfoService) GetStakingCandidates(ctx context.Context, height uint64) (candidateListAll *iotextypes.CandidateListV2, err error) { 104 | err = s.Connect() 105 | if err != nil { 106 | return nil, err 107 | } 108 | candidateListAll = &iotextypes.CandidateListV2{} 109 | for i := uint32(0); ; i++ { 110 | offset := i * readCandidatesLimit 111 | size := uint32(readCandidatesLimit) 112 | candidateList, err := s.getStakingCandidates(ctx, offset, size, height) 113 | if err != nil { 114 | return nil, errors.Wrap(err, "failed to get candidates") 115 | } 116 | candidateListAll.Candidates = append(candidateListAll.Candidates, candidateList.Candidates...) 117 | if len(candidateList.Candidates) < readCandidatesLimit { 118 | break 119 | } 120 | } 121 | return 122 | } 123 | 124 | func (s *getInfoService) getStakingBuckets(ctx context.Context, offset, limit uint32, height uint64) (voteBucketList *iotextypes.VoteBucketList, err error) { 125 | methodName, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{ 126 | Method: iotexapi.ReadStakingDataMethod_BUCKETS, 127 | }) 128 | if err != nil { 129 | return nil, err 130 | } 131 | arg, err := proto.Marshal(&iotexapi.ReadStakingDataRequest{ 132 | Request: &iotexapi.ReadStakingDataRequest_Buckets{ 133 | Buckets: &iotexapi.ReadStakingDataRequest_VoteBuckets{ 134 | Pagination: &iotexapi.PaginationParam{ 135 | Offset: offset, 136 | Limit: limit, 137 | }, 138 | }, 139 | }, 140 | }) 141 | if err != nil { 142 | return nil, err 143 | } 144 | readStateRequest := &iotexapi.ReadStateRequest{ 145 | ProtocolID: []byte(protocolID), 146 | MethodName: methodName, 147 | Arguments: [][]byte{arg}, 148 | Height: fmt.Sprintf("%d", height), 149 | } 150 | readStateRes, err := s.ReadOnlyClient().API().ReadState(ctx, readStateRequest) 151 | if err != nil { 152 | return 153 | } 154 | voteBucketList = &iotextypes.VoteBucketList{} 155 | if err := proto.Unmarshal(readStateRes.GetData(), voteBucketList); err != nil { 156 | return nil, errors.Wrap(err, "failed to unmarshal VoteBucketList") 157 | } 158 | return 159 | } 160 | 161 | func (s *getInfoService) getStakingCandidates(ctx context.Context, offset, limit uint32, height uint64) (candidateList *iotextypes.CandidateListV2, err error) { 162 | methodName, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{ 163 | Method: iotexapi.ReadStakingDataMethod_CANDIDATES, 164 | }) 165 | if err != nil { 166 | return nil, err 167 | } 168 | arg, err := proto.Marshal(&iotexapi.ReadStakingDataRequest{ 169 | Request: &iotexapi.ReadStakingDataRequest_Candidates_{ 170 | Candidates: &iotexapi.ReadStakingDataRequest_Candidates{ 171 | Pagination: &iotexapi.PaginationParam{ 172 | Offset: offset, 173 | Limit: limit, 174 | }, 175 | }, 176 | }, 177 | }) 178 | if err != nil { 179 | return nil, err 180 | } 181 | readStateRequest := &iotexapi.ReadStateRequest{ 182 | ProtocolID: []byte(protocolID), 183 | MethodName: methodName, 184 | Arguments: [][]byte{arg}, 185 | Height: fmt.Sprintf("%d", height), 186 | } 187 | readStateRes, err := s.ReadOnlyClient().API().ReadState(ctx, readStateRequest) 188 | if err != nil { 189 | return 190 | } 191 | candidateList = &iotextypes.CandidateListV2{} 192 | if err := proto.Unmarshal(readStateRes.GetData(), candidateList); err != nil { 193 | return nil, errors.Wrap(err, "failed to unmarshal VoteBucketList") 194 | } 195 | return 196 | } 197 | -------------------------------------------------------------------------------- /examples/openoracle/OpenOraclePriceData.abi: -------------------------------------------------------------------------------- 1 | [{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"priorTimestamp","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"messageTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"NotWritten","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":false,"internalType":"string","name":"key","type":"string"},{"indexed":false,"internalType":"uint64","name":"timestamp","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"value","type":"uint64"}],"name":"Write","type":"event"},{"inputs":[{"internalType":"address","name":"source","type":"address"},{"internalType":"string","name":"key","type":"string"}],"name":"get","outputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"source","type":"address"},{"internalType":"string","name":"key","type":"string"}],"name":"getPrice","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"put","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"source","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"}] -------------------------------------------------------------------------------- /examples/openoracle/OpenOraclePriceData.bin: -------------------------------------------------------------------------------- 1 | 608060405234801561001057600080fd5b50610bc1806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806338636e9a14610051578063482a61931461018457806376977a3a146102c9578063fc2525ab14610364575b600080fd5b61010f6004803603604081101561006757600080fd5b810190602081018135600160201b81111561008157600080fd5b82018360208201111561009357600080fd5b803590602001918460018302840111600160201b831117156100b457600080fd5b919390929091602081019035600160201b8111156100d157600080fd5b8201836020820111156100e357600080fd5b803590602001918460018302840111600160201b8311171561010457600080fd5b509092509050610409565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610149578181015183820152602001610131565b50505050905090810190601f1680156101765780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102ad6004803603604081101561019a57600080fd5b810190602081018135600160201b8111156101b457600080fd5b8201836020820111156101c657600080fd5b803590602001918460018302840111600160201b831117156101e757600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b81111561023957600080fd5b82018360208201111561024b57600080fd5b803590602001918460018302840111600160201b8311171561026c57600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061043f945050505050565b604080516001600160a01b039092168252519081900360200190f35b610347600480360360408110156102df57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561030957600080fd5b82018360208201111561031b57600080fd5b803590602001918460018302840111600160201b8311171561033c57600080fd5b50909250905061052c565b6040805167ffffffffffffffff9092168252519081900360200190f35b6103e26004803603604081101561037a57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156103a457600080fd5b8201836020820111156103b657600080fd5b803590602001918460018302840111600160201b831117156103d757600080fd5b509092509050610586565b6040805167ffffffffffffffff938416815291909216602082015281519081900390910190f35b60606000806060600061041e898989896105f6565b9350935093509350610432848484846108c5565b9998505050505050505050565b60008060008084806020019051606081101561045a57600080fd5b5080516020808301516040938401518a518b84012085517f19457468657265756d205369676e6564204d6573736167653a0a33320000000081860152603c8082019290925286518082039092018252605c81018088528251928601929092206000909252607c810180885282905260ff8316609c82015260bc810186905260dc8101849052955194985091965094509260019260fc8083019392601f198301929081900390910190855afa158015610516573d6000803e3d6000fd5b5050604051601f19015198975050505050505050565b6001600160a01b03831660009081526020819052604080822090518490849080838380828437919091019485525050604051928390036020019092205467ffffffffffffffff600160401b90910416925050509392505050565b6000806000806000876001600160a01b03166001600160a01b03168152602001908152602001600020858560405180838380828437919091019485525050604051928390036020019092205467ffffffffffffffff8082169650600160401b909104169350505050935093915050565b600080606060008061067189898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8d018190048102820181019092528b815292508b91508a908190840183828082843760009201919091525061043f92505050565b905060606000606060008c8c608081101561068b57600080fd5b810190602081018135600160201b8111156106a557600080fd5b8201836020820111156106b757600080fd5b803590602001918460018302840111600160201b831117156106d857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929567ffffffffffffffff853516959094909350604081019250602001359050600160201b81111561073c57600080fd5b82018360208201111561074e57600080fd5b803590602001918460018302840111600160201b8311171561076f57600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516570726963657360d01b60208083019190915282516006818403018152602683019093528251928101929092208a519a9e50989c50939a50933567ffffffffffffffff16985095968b96506046909201945084935050908401908083835b602083106108245780518252601f199092019160209182019101610805565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405160208183030381529060405280519060200120146108b1576040805162461bcd60e51b815260206004820152601d60248201527f4b696e64206f662064617461206d757374206265202770726963657327000000604482015290519081900360640190fd5b939c919b5099509197509095505050505050565b6001600160a01b038416600090815260208181526040808320905185516060949387929182918401908083835b602083106109115780518252601f1990920191602091820191016108f2565b51815160209384036101000a60001901801990921691161790529201948552506040519384900301909220805490935067ffffffffffffffff90811690881611915050801561096d575042610e10018567ffffffffffffffff16105b801561098157506001600160a01b03861615155b15610b345760405180604001604052808667ffffffffffffffff1681526020018467ffffffffffffffff16815250600080886001600160a01b03166001600160a01b03168152602001908152602001600020856040518082805190602001908083835b60208310610a035780518252601f1990920191602091820191016109e4565b51815160209384036101000a6000190180199092169116179052920194855250604080519485900382018520865181549784015167ffffffffffffffff1990981667ffffffffffffffff918216176fffffffffffffffff00000000000000001916600160401b988216989098029790971790558a861685830152948816948401949094525050606080825286519082015285516001600160a01b038916927f4d3f5aa96531b83f5389343ecd20cd8ac1fba33b64634c1b547a4d85d31540d39288928a92899291829160808301919087019080838360005b83811015610af3578181015183820152602001610adb565b50505050905090810190601f168015610b205780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a2610b81565b80546040805167ffffffffffffffff928316815291871660208301524282820152517f7d218dba44a461fb2d7b5fe792128439313d3c48c86d4c3e4981a8eaca831a769181900360600190a15b509194935050505056fea2646970667358221220f4eb2d7e04a1436ec32d1137e824dbe96a5f42f3ef13adfba070f7c3de66d6fe64736f6c634300060a0033 -------------------------------------------------------------------------------- /examples/openoracle/README.md: -------------------------------------------------------------------------------- 1 | ### 1. Get Open Oracle Contracts 2 | 3 | ``` 4 | git clone https://github.com/compound-finance/open-oracle.git 5 | ``` 6 | 7 | ### 2. Install ioctl and solc 8 | 9 | ``` 10 | wget https://github.com/ethereum/solidity/releases/download/v0.6.10/solc-static-linux 11 | 12 | chmod +x solc-static-linux 13 | 14 | cp solc-static-linux /usr/local/bin/solc 15 | 16 | curl https://raw.githubusercontent.com/iotexproject/iotex-core/master/install-cli.sh | sh -s "unstable" 17 | ``` 18 | 19 | ### 3. Compile OpenOraclePriceData Contract 20 | 21 | ``` 22 | ioctl contract compile OpenOraclePriceData --abi-out OpenOraclePriceData.abi --bin-out OpenOraclePriceData.bin 23 | ``` 24 | 25 | ### 4. Create and Set Private Key 26 | 27 | ``` 28 | ioctl account create 29 | ... 30 | 31 | export PrivateKey=d0bd45f30f5efea...7a498 32 | ``` 33 | Get some testnet IOTX from https://faucet.iotex.io/ for the created account. 34 | 35 | 36 | ### 5. Run 37 | ``` 38 | ./openoracle 39 | ``` 40 | -------------------------------------------------------------------------------- /examples/openoracle/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package main 8 | 9 | import ( 10 | "context" 11 | "encoding/hex" 12 | "fmt" 13 | "io/ioutil" 14 | "math/big" 15 | "os" 16 | ) 17 | 18 | var ( 19 | gasPrice, _ = big.NewInt(0).SetString("1000000000000", 10) 20 | gasLimit = uint64(1000000) 21 | ) 22 | 23 | func main() { 24 | PrivateKey := os.Getenv("PrivateKey") 25 | if PrivateKey == "" { 26 | fmt.Println("Environment Variable [PrivateKey] not defined") 27 | return 28 | } 29 | 30 | bin, err := ioutil.ReadFile("OpenOraclePriceData.bin") 31 | if err != nil { 32 | fmt.Println("OpenOraclePriceData.bin not found") 33 | return 34 | } 35 | 36 | abi, err := ioutil.ReadFile("OpenOraclePriceData.abi") 37 | if err != nil { 38 | fmt.Println("OpenOraclePriceData.abi not found") 39 | return 40 | } 41 | 42 | s, err := NewOpenOracleService(PrivateKey, string(abi), string(bin), "", gasPrice, gasLimit, "api.testnet.iotex.one:80", false) 43 | if err != nil { 44 | fmt.Println(err) 45 | return 46 | } 47 | r, err := s.Deploy(context.Background(), true) 48 | fmt.Println("Contract deployed, action hash: ", r, err) 49 | 50 | writeClient, err := NewOpenOracleService(PrivateKey, string(abi), string(bin), "io1s50xy46vjtneh5m8jv6ync75m9vlj28qe0pr26", gasPrice, gasLimit, "api.testnet.iotex.one:80", false) 51 | if err != nil { 52 | fmt.Println(err) 53 | return 54 | } 55 | 56 | // get some data from coinbase,curl https://api.pro.coinbase.com/oracle, have to be latest data or it will fail 57 | mes := "0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000005f45b50000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000002a1ec56780000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034254430000000000000000000000000000000000000000000000000000000000" 58 | sig := "187a858734a49fd7ffaa5ba57a44cf12dd4dbb73e142e6f70a3b3e27d9d00a8bb8e430c650eb58b33a79c5ef9ec8d3bf1b3937015a3dbbb00f50cce07b90e8a6000000000000000000000000000000000000000000000000000000000000001c" 59 | testMessage, err := hex.DecodeString(mes) 60 | if err != nil { 61 | fmt.Println(err) 62 | return 63 | } 64 | 65 | testSignature, err := hex.DecodeString(sig) 66 | if err != nil { 67 | fmt.Println(err) 68 | return 69 | } 70 | 71 | hash, err := writeClient.Put(context.Background(), testMessage, testSignature) 72 | fmt.Println("Invoke contract via action: ", hash, err) 73 | 74 | readClient, err := NewOpenOracleService("", string(abi), string(bin), "io1s50xy46vjtneh5m8jv6ync75m9vlj28qe0pr26", gasPrice, gasLimit, "api.testnet.iotex.one:80", false) 75 | if err != nil { 76 | fmt.Println(err) 77 | return 78 | } 79 | 80 | // io1ln4d4743f4rwyq2y7jyzf5xqnvdq8u4u9hma6k - 0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC 81 | ret, err := readClient.Get(context.Background(), "io1ln4d4743f4rwyq2y7jyzf5xqnvdq8u4u9hma6k", "BTC") 82 | fmt.Println("Read contract: ", ret, err) 83 | } 84 | -------------------------------------------------------------------------------- /examples/openoracle/service.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package main 8 | 9 | import ( 10 | "context" 11 | "encoding/hex" 12 | "errors" 13 | "fmt" 14 | "math/big" 15 | "strings" 16 | "time" 17 | 18 | "github.com/ethereum/go-ethereum/accounts/abi" 19 | 20 | "github.com/iotexproject/iotex-address/address" 21 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 22 | 23 | "github.com/iotexproject/iotex-antenna-go/v2/examples/util" 24 | ) 25 | 26 | // OpenOracleService is the OpenOracleService interface 27 | type OpenOracleService interface { 28 | // Deploy is the Deploy interface 29 | Deploy(ctx context.Context, waitContractAddress bool, args ...interface{}) (string, error) 30 | // Put is the Put interface 31 | Put(ctx context.Context, message []byte, signature []byte) (string, error) 32 | // Get is the Get interface 33 | Get(ctx context.Context, source, key string) (string, error) 34 | } 35 | 36 | type openOracleService struct { 37 | util.IotexService 38 | 39 | contract address.Address 40 | abi abi.ABI 41 | bin string 42 | gasPrice *big.Int 43 | gasLimit uint64 44 | } 45 | 46 | // NewOpenOracleService returns OpenOracleService 47 | func NewOpenOracleService(accountPrivate, abiString, binString, contract string, gasPrice *big.Int, gasLimit uint64, endpoint string, secure bool) (OpenOracleService, error) { 48 | abi, err := abi.JSON(strings.NewReader(abiString)) 49 | if err != nil { 50 | return nil, err 51 | } 52 | var addr address.Address 53 | if contract != "" { 54 | addr, err = address.FromString(contract) 55 | if err != nil { 56 | return nil, err 57 | } 58 | } 59 | 60 | return &openOracleService{ 61 | util.NewIotexService(accountPrivate, endpoint, secure), 62 | addr, abi, binString, gasPrice, gasLimit, 63 | }, nil 64 | } 65 | 66 | // Deploy is the Deploy interface 67 | func (s *openOracleService) Deploy(ctx context.Context, waitContractAddress bool, args ...interface{}) (hash string, err error) { 68 | err = s.Connect() 69 | if err != nil { 70 | return 71 | } 72 | data, err := hex.DecodeString(s.bin) 73 | if err != nil { 74 | return 75 | } 76 | h, err := s.AuthClient().DeployContract(data).SetGasPrice(s.gasPrice).SetGasLimit(s.gasLimit).SetArgs(s.abi, args...).Call(ctx) 77 | if err != nil { 78 | return 79 | } 80 | hash = hex.EncodeToString(h[:]) 81 | if waitContractAddress { 82 | time.Sleep(time.Second * 10) 83 | receiptResponse, err := s.AuthClient().GetReceipt(h).Call(ctx) 84 | if err != nil { 85 | return "", err 86 | } 87 | status := receiptResponse.GetReceiptInfo().GetReceipt().GetStatus() 88 | if status != uint64(iotextypes.ReceiptStatus_Success) { 89 | return "", errors.New("deploy error,status:" + fmt.Sprintf("%d", status)) 90 | } 91 | addr := receiptResponse.GetReceiptInfo().GetReceipt().GetContractAddress() 92 | s.contract, err = address.FromString(addr) 93 | if err != nil { 94 | return "", err 95 | } 96 | } 97 | return 98 | } 99 | 100 | // Put is the Put interface 101 | func (s *openOracleService) Put(ctx context.Context, message, signature []byte) (hash string, err error) { 102 | err = s.Connect() 103 | if err != nil { 104 | return 105 | } 106 | h, err := s.AuthClient().Contract(s.contract, s.abi).Execute("put", message, signature).SetGasPrice(s.gasPrice).SetGasLimit(s.gasLimit).Call(ctx) 107 | if err != nil { 108 | return 109 | } 110 | hash = hex.EncodeToString(h[:]) 111 | return 112 | } 113 | 114 | // Get is the Get interface 115 | func (s *openOracleService) Get(ctx context.Context, source, key string) (ret string, err error) { 116 | err = s.Connect() 117 | if err != nil { 118 | return 119 | } 120 | data, err := s.ReadOnlyClient().ReadOnlyContract(s.contract, s.abi).Read("get", source, key).Call(ctx) 121 | if err != nil { 122 | return 123 | } 124 | ret = hex.EncodeToString(data.Raw) 125 | return 126 | } 127 | -------------------------------------------------------------------------------- /examples/util/service.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package util 8 | 9 | import ( 10 | "crypto/tls" 11 | "sync" 12 | 13 | "google.golang.org/grpc" 14 | "google.golang.org/grpc/connectivity" 15 | "google.golang.org/grpc/credentials" 16 | 17 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 18 | 19 | "github.com/iotexproject/iotex-antenna-go/v2/account" 20 | "github.com/iotexproject/iotex-antenna-go/v2/iotex" 21 | ) 22 | 23 | // IotexService is the IotexService interface 24 | type IotexService interface { 25 | // Connect connect to iotex server 26 | Connect() error 27 | // AuthClient is the client with private key 28 | AuthClient() iotex.AuthedClient 29 | // ReadOnlyClient is the client without private key 30 | ReadOnlyClient() iotex.ReadOnlyClient 31 | } 32 | 33 | type iotexService struct { 34 | sync.RWMutex 35 | endpoint string 36 | secure bool 37 | accountPrivate string 38 | 39 | grpcConn *grpc.ClientConn 40 | authedClient iotex.AuthedClient 41 | readOnlyClient iotex.ReadOnlyClient 42 | } 43 | 44 | // NewIotexService returns IotexService 45 | func NewIotexService(accountPrivate, endpoint string, secure bool) IotexService { 46 | return &iotexService{ 47 | endpoint: endpoint, 48 | secure: secure, 49 | accountPrivate: accountPrivate, 50 | } 51 | } 52 | 53 | // Connect connect to iotex server 54 | func (s *iotexService) Connect() (err error) { 55 | return s.connect() 56 | } 57 | 58 | // AuthClient is the client with private key 59 | func (s *iotexService) AuthClient() iotex.AuthedClient { 60 | return s.authedClient 61 | } 62 | 63 | // AuthClient is the client without private key 64 | func (s *iotexService) ReadOnlyClient() iotex.ReadOnlyClient { 65 | return s.readOnlyClient 66 | } 67 | 68 | func (s *iotexService) connect() (err error) { 69 | s.Lock() 70 | defer s.Unlock() 71 | // Check if the existing connection is good. 72 | if s.grpcConn != nil && s.grpcConn.GetState() != connectivity.Shutdown { 73 | return 74 | } 75 | opts := []grpc.DialOption{} 76 | if s.secure { 77 | opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) 78 | } else { 79 | opts = append(opts, grpc.WithInsecure()) 80 | } 81 | s.grpcConn, err = grpc.Dial(s.endpoint, opts...) 82 | if err != nil { 83 | return 84 | } 85 | if s.accountPrivate != "" { 86 | creator, err := account.HexStringToAccount(s.accountPrivate) 87 | if err != nil { 88 | return err 89 | } 90 | s.authedClient = iotex.NewAuthedClient(iotexapi.NewAPIServiceClient(s.grpcConn), 1, creator) 91 | } 92 | 93 | s.readOnlyClient = iotex.NewReadOnlyClient(iotexapi.NewAPIServiceClient(s.grpcConn)) 94 | return 95 | } 96 | -------------------------------------------------------------------------------- /examples/xrc20tokens/README.md: -------------------------------------------------------------------------------- 1 | ### 1. Install ioctl and solc 2 | 3 | ``` 4 | wget https://github.com/ethereum/solidity/releases/download/v0.5.16/solc-static-linux 5 | 6 | chmod +x solc-static-linux 7 | 8 | cp solc-static-linux /usr/local/bin/solc 9 | 10 | curl https://raw.githubusercontent.com/iotexproject/iotex-core/master/install-cli.sh | sh -s "unstable" 11 | ``` 12 | 13 | ### 2. Compile XRC20 Contract using ioctl 14 | 15 | ``` 16 | ioctl contract compile XRC20 --abi-out XRC20.abi --bin-out XRC20.bin 17 | ``` 18 | 19 | 20 | ### 3. Create and Set Private Key 21 | 22 | ``` 23 | ioctl account create 24 | ... 25 | 26 | export PrivateKey=d0bd45f30f5efea...7a498 27 | ``` 28 | 29 | Get some testnet IOTX from https://faucet.iotex.io/ for the created account. 30 | 31 | ### 4. Run 32 | 33 | ``` 34 | ./xrc20tokens 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/xrc20tokens/XRC20.abi: -------------------------------------------------------------------------------- 1 | [{"inputs":[{"internalType":"uint256","name":"initialSupply","type":"uint256"},{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"keys","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /examples/xrc20tokens/XRC20.bin: -------------------------------------------------------------------------------- 1 | 60806040526002805460ff1916601217905534801561001d57600080fd5b5060405161092f38038061092f8339818101604052606081101561004057600080fd5b81516020830180516040519294929383019291908464010000000082111561006757600080fd5b90830190602082018581111561007c57600080fd5b825164010000000081118282018810171561009657600080fd5b82525081516020918201929091019080838360005b838110156100c35781810151838201526020016100ab565b50505050905090810190601f1680156100f05780820380516001836020036101000a031916815260200191505b506040526020018051604051939291908464010000000082111561011357600080fd5b90830190602082018581111561012857600080fd5b825164010000000081118282018810171561014257600080fd5b82525081516020918201929091019080838360005b8381101561016f578181015183820152602001610157565b50505050905090810190601f16801561019c5780820380516001836020036101000a031916815260200191505b50604090815260025460ff16600a0a87026003819055336000908152600460209081529281209190915586516101da955090935090860191506101f7565b5080516101ee9060019060208401906101f7565b50505050610292565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023857805160ff1916838001178555610265565b82800160010185558215610265579182015b8281111561026557825182559160200191906001019061024a565b50610271929150610275565b5090565b61028f91905b80821115610271576000815560010161027b565b90565b61068e806102a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806342966c681161007157806342966c68146101d9578063670d14b2146101f657806370a082311461021c57806395d89b4114610242578063a9059cbb1461024a578063dd62ed3e14610278576100a9565b806306fdde03146100ae578063095ea7b31461012b57806318160ddd1461016b57806323b872dd14610185578063313ce567146101bb575b600080fd5b6100b66102a6565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100f05781810151838201526020016100d8565b50505050905090810190601f16801561011d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101576004803603604081101561014157600080fd5b506001600160a01b038135169060200135610334565b604080519115158252519081900360200190f35b610173610361565b60408051918252519081900360200190f35b6101576004803603606081101561019b57600080fd5b506001600160a01b03813581169160208101359091169060400135610367565b6101c36103d6565b6040805160ff9092168252519081900360200190f35b610157600480360360208110156101ef57600080fd5b50356103df565b6100b66004803603602081101561020c57600080fd5b50356001600160a01b0316610457565b6101736004803603602081101561023257600080fd5b50356001600160a01b03166104bf565b6100b66104d1565b6102766004803603604081101561026057600080fd5b506001600160a01b03813516906020013561052b565b005b6101736004803603604081101561028e57600080fd5b506001600160a01b038135811691602001351661053a565b6000805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561032c5780601f106103015761010080835404028352916020019161032c565b820191906000526020600020905b81548152906001019060200180831161030f57829003601f168201915b505050505081565b3360009081526005602090815260408083206001600160a01b039590951683529390529190912055600190565b60035481565b6001600160a01b038316600090815260056020908152604080832033845290915281205482111561039757600080fd5b6001600160a01b03841660009081526005602090815260408083203384529091529020805483900390556103cc848484610557565b5060019392505050565b60025460ff1681565b336000908152600460205260408120548211156103fb57600080fd5b3360008181526004602090815260409182902080548690039055600380548690039055815185815291517fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca59281900390910190a2506001919050565b60066020908152600091825260409182902080548351601f60026000196101006001861615020190931692909204918201849004840281018401909452808452909183018282801561032c5780601f106103015761010080835404028352916020019161032c565b60046020526000908152604090205481565b60018054604080516020600284861615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561032c5780601f106103015761010080835404028352916020019161032c565b610536338383610557565b5050565b600560209081526000928352604080842090915290825290205481565b6001600160a01b03821661056a57600080fd5b6001600160a01b03831660009081526004602052604090205481111561058f57600080fd5b6001600160a01b038216600090815260046020526040902054818101116105b557600080fd5b6001600160a01b038083166000818152600460209081526040808320805495891680855282852080548981039091559486905281548801909155815187815291519390950194927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a36001600160a01b0380841660009081526004602052604080822054928716825290205401811461065357fe5b5050505056fea265627a7a72315820857a162e390893dab966d1c5cd568d42e04683e3ee4f7bf0542ee3b49e55fc4564736f6c63430005100032 -------------------------------------------------------------------------------- /examples/xrc20tokens/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package main 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | "io/ioutil" 13 | "math/big" 14 | "os" 15 | "time" 16 | ) 17 | 18 | var ( 19 | gasPrice, _ = big.NewInt(0).SetString("1000000000000", 10) 20 | gasLimit = uint64(1000000) 21 | ) 22 | 23 | func main() { 24 | PrivateKey := os.Getenv("PrivateKey") 25 | if PrivateKey == "" { 26 | fmt.Println("Environment Variable [PrivateKey] not defined") 27 | return 28 | } 29 | 30 | bin, err := ioutil.ReadFile("XRC20.bin") 31 | if err != nil { 32 | fmt.Println("XRC20.bin not found") 33 | return 34 | } 35 | 36 | abi, err := ioutil.ReadFile("XRC20.abi") 37 | if err != nil { 38 | fmt.Println("XRC20.abi not found") 39 | return 40 | } 41 | 42 | s, err := NewXrc20Service(PrivateKey, string(abi), string(bin), "", gasPrice, gasLimit, "api.testnet.iotex.one:80", false) 43 | if err != nil { 44 | fmt.Println(err) 45 | return 46 | } 47 | 48 | r, err := s.Deploy(context.Background(), true, big.NewInt(2000000), "IOTX", "IOTX") 49 | if err != nil { 50 | fmt.Println(err) 51 | return 52 | } 53 | fmt.Println("Contract deployed, action hash:", r) 54 | 55 | r, err = s.Transfer(context.Background(), "io1zk6gqq0m2z9ytlu77t76e3632ezy39fa83xjnn", big.NewInt(10)) 56 | if err != nil { 57 | fmt.Println(err) 58 | return 59 | } 60 | fmt.Println("Token transfer completed: ", r) 61 | 62 | time.Sleep(time.Second * 10) 63 | b, err := s.BalanceOf(context.Background(), "io1zk6gqq0m2z9ytlu77t76e3632ezy39fa83xjnn") 64 | if err != nil { 65 | fmt.Println(err) 66 | return 67 | } 68 | fmt.Println("Balance of token: ", b) 69 | } 70 | -------------------------------------------------------------------------------- /examples/xrc20tokens/service.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package main 8 | 9 | import ( 10 | "context" 11 | "encoding/hex" 12 | "errors" 13 | "fmt" 14 | "math/big" 15 | "strings" 16 | "time" 17 | 18 | "github.com/ethereum/go-ethereum/accounts/abi" 19 | "github.com/ethereum/go-ethereum/common" 20 | 21 | "github.com/iotexproject/iotex-address/address" 22 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 23 | 24 | "github.com/iotexproject/iotex-antenna-go/v2/examples/util" 25 | ) 26 | 27 | // Xrc20Service is the Xrc20Service interface 28 | type Xrc20Service interface { 29 | // Deploy is the Deploy interface 30 | Deploy(ctx context.Context, waitContractAddress bool, args ...interface{}) (string, error) 31 | // Transfer is the Transfer interface 32 | Transfer(ctx context.Context, to string, amount *big.Int) (string, error) 33 | // BalanceOf is the BalanceOf interface 34 | BalanceOf(ctx context.Context, addr string) (*big.Int, error) 35 | } 36 | 37 | type xrc20Service struct { 38 | util.IotexService 39 | 40 | contract address.Address 41 | abi abi.ABI 42 | bin string 43 | gasPrice *big.Int 44 | gasLimit uint64 45 | } 46 | 47 | // NewXrc20Service returns Xrc20Service 48 | func NewXrc20Service(accountPrivate, abiString, binString, contract string, gasPrice *big.Int, gasLimit uint64, endpoint string, secure bool) (Xrc20Service, error) { 49 | abi, err := abi.JSON(strings.NewReader(abiString)) 50 | if err != nil { 51 | return nil, err 52 | } 53 | var addr address.Address 54 | if contract != "" { 55 | addr, err = address.FromString(contract) 56 | if err != nil { 57 | return nil, err 58 | } 59 | } 60 | return &xrc20Service{ 61 | util.NewIotexService(accountPrivate, endpoint, secure), 62 | addr, abi, binString, gasPrice, gasLimit, 63 | }, nil 64 | } 65 | 66 | // Deploy is the Deploy interface 67 | func (s *xrc20Service) Deploy(ctx context.Context, waitContractAddress bool, args ...interface{}) (hash string, err error) { 68 | err = s.Connect() 69 | if err != nil { 70 | return 71 | } 72 | data, err := hex.DecodeString(s.bin) 73 | if err != nil { 74 | return 75 | } 76 | h, err := s.AuthClient().DeployContract(data).SetGasPrice(s.gasPrice).SetGasLimit(s.gasLimit).SetArgs(s.abi, args...).Call(ctx) 77 | if err != nil { 78 | return 79 | } 80 | hash = hex.EncodeToString(h[:]) 81 | if waitContractAddress { 82 | time.Sleep(time.Second * 10) 83 | receiptResponse, err := s.AuthClient().GetReceipt(h).Call(ctx) 84 | if err != nil { 85 | return "", err 86 | } 87 | status := receiptResponse.GetReceiptInfo().GetReceipt().GetStatus() 88 | if status != uint64(iotextypes.ReceiptStatus_Success) { 89 | return "", errors.New("deploy error,status:" + fmt.Sprintf("%d", status)) 90 | } 91 | addr := receiptResponse.GetReceiptInfo().GetReceipt().GetContractAddress() 92 | s.contract, err = address.FromString(addr) 93 | if err != nil { 94 | return "", err 95 | } 96 | } 97 | return 98 | } 99 | 100 | // Transfer is the Transfer interface 101 | func (s *xrc20Service) Transfer(ctx context.Context, to string, amount *big.Int) (hash string, err error) { 102 | err = s.Connect() 103 | if err != nil { 104 | return 105 | } 106 | addr, err := address.FromString(to) 107 | if err != nil { 108 | return 109 | } 110 | ethAddr := common.HexToAddress(hex.EncodeToString(addr.Bytes())) 111 | h, err := s.AuthClient().Contract(s.contract, s.abi).Execute("transfer", ethAddr, amount).SetGasPrice(s.gasPrice).SetGasLimit(s.gasLimit).Call(ctx) 112 | if err != nil { 113 | return 114 | } 115 | hash = hex.EncodeToString(h[:]) 116 | return 117 | } 118 | 119 | // BalanceOf is the BalanceOf interface 120 | func (s *xrc20Service) BalanceOf(ctx context.Context, addr string) (balance *big.Int, err error) { 121 | err = s.Connect() 122 | if err != nil { 123 | return 124 | } 125 | ret, err := s.ReadOnlyClient().ReadOnlyContract(s.contract, s.abi).Read("balanceOf", addr).Call(ctx) 126 | if err != nil { 127 | return 128 | } 129 | balance = new(big.Int).SetBytes(ret.Raw) 130 | return 131 | } 132 | -------------------------------------------------------------------------------- /examples/xrc20tokens/xrc20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.22 <0.6.0; 2 | 3 | contract XRC20 { 4 | // Public variables of the token 5 | string public name; 6 | string public symbol; 7 | uint8 public decimals = 18; 8 | // 18 decimals is the strongly suggested default, avoid changing it 9 | uint256 public totalSupply; 10 | 11 | // This creates an array with all balances 12 | mapping (address => uint256) public balanceOf; 13 | mapping (address => mapping (address => uint256)) public allowance; 14 | mapping (address => string) public keys; 15 | 16 | // This generates a public event on the blockchain that will notify clients 17 | event Transfer(address indexed from, address indexed to, uint256 value); 18 | // This notifies clients about the amount burnt 19 | event Burn(address indexed from, uint256 value); 20 | 21 | /** 22 | * Constrctor function 23 | * 24 | * Initializes contract with initial supply tokens to the creator of the contract 25 | */ 26 | constructor(uint256 initialSupply,string memory tokenName,string memory tokenSymbol) public { 27 | totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount 28 | balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens 29 | name = tokenName; // Set the name for display purposes 30 | symbol = tokenSymbol; // Set the symbol for display purposes 31 | } 32 | 33 | /** 34 | * Internal transfer, only can be called by this contract 35 | */ 36 | function _transfer(address _from, address _to, uint _value) internal { 37 | // Prevent transfer to 0x0 address. Use burn() instead 38 | require(_to != address(0)); 39 | // Check if the sender has enough 40 | require(balanceOf[_from] >= _value); 41 | // Check for overflows 42 | require(balanceOf[_to] + _value > balanceOf[_to]); 43 | // Save this for an assertion in the future 44 | uint previousBalances = balanceOf[_from] + balanceOf[_to]; 45 | // Subtract from the sender 46 | balanceOf[_from] -= _value; 47 | // Add the same to the recipient 48 | balanceOf[_to] += _value; 49 | emit Transfer(_from, _to, _value); 50 | // Asserts are used to use static analysis to find bugs in your code. They should never fail 51 | assert(balanceOf[_from] + balanceOf[_to] == previousBalances); 52 | } 53 | 54 | /** 55 | * Transfer tokens 56 | * 57 | * Send `_value` tokens to `_to` from your account 58 | * 59 | * @param _to The address of the recipient 60 | * @param _value the amount to send 61 | */ 62 | function transfer(address _to, uint256 _value) public { 63 | _transfer(msg.sender, _to, _value); 64 | } 65 | 66 | /** 67 | * Transfer tokens from other address 68 | * 69 | * Send `_value` tokens to `_to` in behalf of `_from` 70 | * 71 | * @param _from The address of the sender 72 | * @param _to The address of the recipient 73 | * @param _value the amount to send 74 | */ 75 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 76 | require(_value <= allowance[_from][msg.sender]); // Check allowance 77 | allowance[_from][msg.sender] -= _value; 78 | _transfer(_from, _to, _value); 79 | return true; 80 | } 81 | 82 | /** 83 | * Set allowance for other address 84 | * 85 | * Allows `_spender` to spend no more than `_value` tokens in your behalf 86 | * 87 | * @param _spender The address authorized to spend 88 | * @param _value the max amount they can spend 89 | */ 90 | function approve(address _spender, uint256 _value) public returns (bool success) { 91 | allowance[msg.sender][_spender] = _value; 92 | return true; 93 | } 94 | 95 | /** 96 | * Destroy tokens 97 | * 98 | * Remove `_value` tokens from the system irreversibly 99 | * 100 | * @param _value the amount of money to burn 101 | */ 102 | function burn(uint256 _value) public returns (bool success) { 103 | require(balanceOf[msg.sender] >= _value); // Check if the sender has enough 104 | 105 | balanceOf[msg.sender] -= _value; // Subtract from the sender 106 | totalSupply -= _value; // Updates totalSupply 107 | emit Burn(msg.sender, _value); 108 | 109 | return true; 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /examples/xrc20tokens/xrc20tokens: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotexproject/iotex-antenna-go/b76d65d4789b06714850206d0f341c3276100047/examples/xrc20tokens/xrc20tokens -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/iotexproject/iotex-antenna-go/v2 2 | 3 | go 1.21.11 4 | 5 | require ( 6 | github.com/cenkalti/backoff v2.2.1+incompatible 7 | github.com/ethereum/go-ethereum v1.10.26 8 | github.com/golang-jwt/jwt v3.2.2+incompatible 9 | github.com/golang/mock v1.4.4 10 | github.com/golang/protobuf v1.5.3 11 | github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 12 | github.com/iotexproject/go-pkgs v0.1.13 13 | github.com/iotexproject/iotex-address v0.2.8 14 | github.com/iotexproject/iotex-proto v0.5.10 15 | github.com/pkg/errors v0.9.1 16 | github.com/stretchr/testify v1.8.4 17 | golang.org/x/crypto v0.31.0 18 | google.golang.org/grpc v1.33.1 19 | google.golang.org/protobuf v1.27.1 20 | ) 21 | 22 | require ( 23 | github.com/bits-and-blooms/bitset v1.10.0 // indirect 24 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect 25 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect 26 | github.com/consensys/bavard v0.1.13 // indirect 27 | github.com/consensys/gnark-crypto v0.12.1 // indirect 28 | github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect 29 | github.com/davecgh/go-spew v1.1.1 // indirect 30 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect 31 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect 32 | github.com/dustinxie/gmsm v1.4.0 // indirect 33 | github.com/ethereum/c-kzg-4844 v0.4.0 // indirect 34 | github.com/fsnotify/fsnotify v1.6.0 // indirect 35 | github.com/google/uuid v1.3.0 // indirect 36 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect 37 | github.com/holiman/uint256 v1.2.4 // indirect 38 | github.com/mmcloughlin/addchain v0.4.0 // indirect 39 | github.com/pmezard/go-difflib v1.0.0 // indirect 40 | github.com/supranational/blst v0.3.11 // indirect 41 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect 42 | golang.org/x/net v0.21.0 // indirect 43 | golang.org/x/sync v0.10.0 // indirect 44 | golang.org/x/sys v0.28.0 // indirect 45 | golang.org/x/text v0.21.0 // indirect 46 | google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a // indirect 47 | gopkg.in/yaml.v3 v3.0.1 // indirect 48 | rsc.io/tmplfunc v0.0.3 // indirect 49 | ) 50 | 51 | replace github.com/ethereum/go-ethereum => github.com/iotexproject/go-ethereum v0.5.0 52 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= 5 | github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= 6 | github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= 7 | github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= 8 | github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= 9 | github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= 10 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 11 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 12 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 13 | github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= 14 | github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 15 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= 16 | github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= 17 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= 18 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 19 | github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 20 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 21 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 22 | github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= 23 | github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= 24 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 25 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 26 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 27 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 28 | github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= 29 | github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= 30 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= 31 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= 32 | github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= 33 | github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= 34 | github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= 35 | github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 36 | github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= 37 | github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 38 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= 39 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= 40 | github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= 41 | github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= 42 | github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= 43 | github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= 44 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= 45 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= 46 | github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= 47 | github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 48 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 49 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 50 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 51 | github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= 52 | github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 53 | github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= 54 | github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= 55 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= 56 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= 57 | github.com/dustinxie/gmsm v1.4.0 h1:6nksaLOaQZ3jWsiOngxuLxqF1lMnx1kOkBSzu1q5b5A= 58 | github.com/dustinxie/gmsm v1.4.0/go.mod h1:RXcL1h0Punq69MHL2yZrWYCDFPbqxrXCZiZvZnKjGUI= 59 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 60 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 61 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 62 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 63 | github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= 64 | github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 65 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 66 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 67 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= 68 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= 69 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 70 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 71 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 72 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 73 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 74 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 75 | github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= 76 | github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 77 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 78 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 79 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 80 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 81 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 82 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 83 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 84 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 85 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 86 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 87 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 88 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 89 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 90 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 91 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 92 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 93 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 94 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 95 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 96 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 97 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 98 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= 99 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 100 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 101 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 102 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 103 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 104 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 105 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 106 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 107 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 108 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 109 | github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 110 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 111 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 112 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 113 | github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= 114 | github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= 115 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 116 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 117 | github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= 118 | github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= 119 | github.com/iotexproject/go-ethereum v0.5.0 h1:lpmCpO4AdoFElHvrbmcSict86hHmd1yYSMrwIIhQ08k= 120 | github.com/iotexproject/go-ethereum v0.5.0/go.mod h1:hKL2Qcj1OvStXNSEDbucexqnEt1Wh4Cz329XsjAalZY= 121 | github.com/iotexproject/go-pkgs v0.1.13 h1:bK48DVenkfYkC4TRoqL77RLFRBE1MUfscCW495kzcC8= 122 | github.com/iotexproject/go-pkgs v0.1.13/go.mod h1:t5X9kQ1VL5H+L+DC5GmohXnFKlcxaTcRnIBBuydcsTQ= 123 | github.com/iotexproject/iotex-address v0.2.8 h1:jaTR5pZe/ljiYW4OqHl9NKPs0h1o91Gi6FILOTaBCXw= 124 | github.com/iotexproject/iotex-address v0.2.8/go.mod h1:K78yPSMB4K7gF/iQ7djT1amph0RBrP3rSkFfH7gNG70= 125 | github.com/iotexproject/iotex-proto v0.5.10 h1:+7Hw8KYposo0tJxgIEnPRpKU/TlQGMNn1S0tpSUz6RI= 126 | github.com/iotexproject/iotex-proto v0.5.10/go.mod h1:OfmLvjBmy5EYeLxxDv6kesJq+Mm3Adn5GKgDJgF9G9U= 127 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 128 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 129 | github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= 130 | github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= 131 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 132 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 133 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 134 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 135 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 136 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 137 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 138 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 139 | github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= 140 | github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= 141 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 142 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 143 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= 144 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 145 | github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= 146 | github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= 147 | github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= 148 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 149 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 150 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 151 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 152 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 153 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 154 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 155 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 156 | github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= 157 | github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 158 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 159 | github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= 160 | github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 161 | github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= 162 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 163 | github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= 164 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 165 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 166 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 167 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 168 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 169 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 170 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= 171 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 172 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 173 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 174 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 175 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 176 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 177 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 178 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 179 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 180 | github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= 181 | github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 182 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= 183 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 184 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 185 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 186 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 187 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 188 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 189 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 190 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 191 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 192 | golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 193 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 194 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 195 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 196 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 197 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= 198 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 199 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 200 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 201 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 202 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 203 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 204 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 205 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 206 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 207 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 208 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 209 | golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= 210 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 211 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 212 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 213 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 214 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 215 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 216 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 217 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 218 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 219 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 220 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 221 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 222 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 223 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 224 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 225 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 226 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 227 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 228 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 229 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 230 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 231 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 232 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 233 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 234 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 235 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 236 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 237 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 238 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 239 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 240 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 241 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 242 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 243 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 244 | google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a h1:GnJAhasbD8HiT8DZMvsEx3QLVy/X0icq/MGr0MqRJ2M= 245 | google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 246 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 247 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 248 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 249 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 250 | google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= 251 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 252 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 253 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 254 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 255 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 256 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 257 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 258 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 259 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 260 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 261 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 262 | google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= 263 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 264 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 265 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 266 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 267 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 268 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 269 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 270 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 271 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 272 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 273 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 274 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 275 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 276 | rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= 277 | rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= 278 | -------------------------------------------------------------------------------- /iotex/caller_claimreward.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package iotex 8 | 9 | import ( 10 | "context" 11 | "math/big" 12 | 13 | "github.com/iotexproject/go-pkgs/hash" 14 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 15 | "google.golang.org/grpc" 16 | 17 | "github.com/iotexproject/iotex-antenna-go/v2/errcodes" 18 | ) 19 | 20 | type claimRewardCaller struct { 21 | *sendActionCaller 22 | amount *big.Int 23 | } 24 | 25 | func (c *claimRewardCaller) SetData(data []byte) ClaimRewardCaller { 26 | c.sendActionCaller.setPayload(data) 27 | return c 28 | } 29 | 30 | func (c *claimRewardCaller) SetGasLimit(g uint64) ClaimRewardCaller { 31 | c.sendActionCaller.setGasLimit(g) 32 | return c 33 | } 34 | 35 | func (c *claimRewardCaller) SetGasPrice(g *big.Int) ClaimRewardCaller { 36 | c.sendActionCaller.setGasPrice(g) 37 | return c 38 | } 39 | 40 | func (c *claimRewardCaller) SetNonce(n uint64) ClaimRewardCaller { 41 | c.sendActionCaller.setNonce(n) 42 | return c 43 | } 44 | 45 | func (c *claimRewardCaller) Call(ctx context.Context, opts ...grpc.CallOption) (hash.Hash256, error) { 46 | if c.amount == nil { 47 | return hash.ZeroHash256, errcodes.New("claim amount cannot be nil", errcodes.InvalidParam) 48 | } 49 | 50 | tx := iotextypes.ClaimFromRewardingFund{ 51 | Amount: c.amount.String(), 52 | Data: c.payload, 53 | } 54 | c.core = &iotextypes.ActionCore{ 55 | Version: ProtocolVersion, 56 | Action: &iotextypes.ActionCore_ClaimFromRewardingFund{ClaimFromRewardingFund: &tx}, 57 | } 58 | return c.sendActionCaller.Call(ctx, opts...) 59 | } 60 | -------------------------------------------------------------------------------- /iotex/caller_contract.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package iotex 8 | 9 | import ( 10 | "context" 11 | "encoding/hex" 12 | "math/big" 13 | "reflect" 14 | 15 | "github.com/ethereum/go-ethereum/accounts/abi" 16 | "github.com/ethereum/go-ethereum/common" 17 | "github.com/iotexproject/go-pkgs/hash" 18 | "github.com/iotexproject/iotex-address/address" 19 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 20 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 21 | "google.golang.org/grpc" 22 | 23 | "github.com/iotexproject/iotex-antenna-go/v2/errcodes" 24 | ) 25 | 26 | type deployContractCaller struct { 27 | *sendActionCaller 28 | abi *abi.ABI 29 | args []interface{} 30 | } 31 | 32 | func (c *deployContractCaller) SetArgs(abi abi.ABI, args ...interface{}) DeployContractCaller { 33 | c.abi = &abi 34 | c.args = args 35 | return c 36 | } 37 | 38 | func (c *deployContractCaller) SetGasLimit(g uint64) DeployContractCaller { 39 | c.sendActionCaller.setGasLimit(g) 40 | return c 41 | } 42 | 43 | func (c *deployContractCaller) SetGasPrice(g *big.Int) DeployContractCaller { 44 | c.sendActionCaller.setGasPrice(g) 45 | return c 46 | } 47 | 48 | func (c *deployContractCaller) SetNonce(n uint64) DeployContractCaller { 49 | c.sendActionCaller.setNonce(n) 50 | return c 51 | } 52 | 53 | func (c *deployContractCaller) Call(ctx context.Context, opts ...grpc.CallOption) (hash.Hash256, error) { 54 | if len(c.payload) == 0 { 55 | return hash.ZeroHash256, errcodes.New("contract data can not empty", errcodes.InvalidParam) 56 | } 57 | if len(c.args) > 0 { 58 | var err error 59 | c.args, err = encodeArgument(c.abi.Constructor, c.args) 60 | if err != nil { 61 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.InvalidParam) 62 | } 63 | packed, err := c.abi.Pack("", c.args...) 64 | if err != nil { 65 | return hash.ZeroHash256, errcodes.New("failed to pack args", errcodes.InvalidParam) 66 | } 67 | c.payload = append(c.payload, packed...) 68 | } 69 | 70 | exec := &iotextypes.Execution{ 71 | Data: c.payload, 72 | Amount: "0", 73 | } 74 | c.core = &iotextypes.ActionCore{ 75 | Version: ProtocolVersion, 76 | Action: &iotextypes.ActionCore_Execution{Execution: exec}, 77 | } 78 | return c.sendActionCaller.Call(ctx, opts...) 79 | } 80 | 81 | type contractArgs struct { 82 | contract address.Address 83 | abi *abi.ABI 84 | method string 85 | args []interface{} 86 | } 87 | 88 | type executeContractCaller struct { 89 | *sendActionCaller 90 | contractArgs 91 | amount *big.Int 92 | } 93 | 94 | func (c *executeContractCaller) SetAmount(a *big.Int) ExecuteContractCaller { 95 | c.amount = a 96 | return c 97 | } 98 | 99 | func (c *executeContractCaller) SetGasLimit(g uint64) ExecuteContractCaller { 100 | c.sendActionCaller.setGasLimit(g) 101 | return c 102 | } 103 | 104 | func (c *executeContractCaller) SetGasPrice(g *big.Int) ExecuteContractCaller { 105 | c.sendActionCaller.setGasPrice(g) 106 | return c 107 | } 108 | 109 | func (c *executeContractCaller) SetNonce(n uint64) ExecuteContractCaller { 110 | c.sendActionCaller.setNonce(n) 111 | return c 112 | } 113 | 114 | func (c *executeContractCaller) Call(ctx context.Context, opts ...grpc.CallOption) (hash.Hash256, error) { 115 | if c.method == "" { 116 | return hash.ZeroHash256, errcodes.New("contract address and method can not empty", errcodes.InvalidParam) 117 | } 118 | 119 | method, exist := c.abi.Methods[c.method] 120 | if !exist { 121 | return hash.ZeroHash256, errcodes.New("method is not found", errcodes.InvalidParam) 122 | } 123 | var err error 124 | c.args, err = encodeArgument(method, c.args) 125 | if err != nil { 126 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.InvalidParam) 127 | } 128 | 129 | c.payload, err = c.abi.Pack(c.method, c.args...) 130 | if err != nil { 131 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.InvalidParam) 132 | } 133 | 134 | exec := &iotextypes.Execution{ 135 | Contract: c.contract.String(), 136 | Data: c.payload, 137 | Amount: "0", 138 | } 139 | if c.amount != nil { 140 | exec.Amount = c.amount.String() 141 | } 142 | c.core = &iotextypes.ActionCore{ 143 | Version: ProtocolVersion, 144 | Action: &iotextypes.ActionCore_Execution{Execution: exec}, 145 | } 146 | return c.sendActionCaller.Call(ctx, opts...) 147 | } 148 | 149 | type readContractCaller struct { 150 | api iotexapi.APIServiceClient 151 | contractArgs 152 | } 153 | 154 | func (c *readContractCaller) Call(ctx context.Context, opts ...grpc.CallOption) (Data, error) { 155 | if c.method == "" { 156 | return Data{}, errcodes.New("contract address and method can not empty", errcodes.InvalidParam) 157 | } 158 | 159 | method, exist := c.abi.Methods[c.method] 160 | if !exist { 161 | return Data{}, errcodes.New("method is not found", errcodes.InvalidParam) 162 | } 163 | var err error 164 | c.args, err = encodeArgument(method, c.args) 165 | if err != nil { 166 | return Data{}, errcodes.NewError(err, errcodes.InvalidParam) 167 | } 168 | 169 | actData, err := c.abi.Pack(c.method, c.args...) 170 | if err != nil { 171 | return Data{}, errcodes.NewError(err, errcodes.InvalidParam) 172 | } 173 | 174 | request := &iotexapi.ReadContractRequest{ 175 | Execution: &iotextypes.Execution{ 176 | Contract: c.contract.String(), 177 | Data: actData, 178 | }, 179 | CallerAddress: address.ZeroAddress, 180 | } 181 | response, err := c.api.ReadContract(ctx, request, opts...) 182 | if err != nil { 183 | return Data{}, errcodes.NewError(err, errcodes.RPCError) 184 | } 185 | 186 | decoded, err := hex.DecodeString(response.GetData()) 187 | if err != nil { 188 | return Data{}, errcodes.NewError(err, errcodes.BadResponse) 189 | } 190 | 191 | return Data{ 192 | method: c.method, 193 | abi: c.abi, 194 | Raw: decoded, 195 | }, nil 196 | } 197 | 198 | func encodeArgument(method abi.Method, args []interface{}) ([]interface{}, error) { 199 | if len(method.Inputs) != len(args) { 200 | return nil, errcodes.New("the number of arguments is not correct", errcodes.InvalidParam) 201 | } 202 | newArgs := make([]interface{}, len(args)) 203 | for index, input := range method.Inputs { 204 | switch input.Type.String() { 205 | case "address": 206 | var err error 207 | newArgs[index], err = addressTypeAssert(args[index]) 208 | if err != nil { 209 | return nil, errcodes.NewError(err, errcodes.InvalidParam) 210 | } 211 | case "address[]": 212 | s := reflect.ValueOf(args[index]) 213 | if s.Kind() != reflect.Slice && s.Kind() != reflect.Array { 214 | return nil, errcodes.New("fail because the type is non-slice, non-array", errcodes.InvalidParam) 215 | } 216 | newArr := make([]common.Address, s.Len()) 217 | for j := 0; j < s.Len(); j++ { 218 | var err error 219 | newArr[j], err = addressTypeAssert(s.Index(j).Interface()) 220 | if err != nil { 221 | return nil, errcodes.NewError(err, errcodes.InvalidParam) 222 | } 223 | } 224 | newArgs[index] = newArr 225 | default: 226 | newArgs[index] = args[index] 227 | } 228 | } 229 | return newArgs, nil 230 | } 231 | -------------------------------------------------------------------------------- /iotex/caller_staking.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package iotex 8 | 9 | import ( 10 | "context" 11 | "math/big" 12 | 13 | "google.golang.org/grpc" 14 | 15 | "github.com/iotexproject/go-pkgs/hash" 16 | "github.com/iotexproject/iotex-address/address" 17 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 18 | 19 | "github.com/iotexproject/iotex-antenna-go/v2/errcodes" 20 | ) 21 | 22 | type ( 23 | stakingCaller struct { 24 | *sendActionCaller 25 | action interface{} 26 | } 27 | 28 | // reclaim to differentiate unstake and withdraw 29 | reclaim struct { 30 | action *iotextypes.StakeReclaim 31 | isWithdraw bool 32 | } 33 | ) 34 | 35 | //Create Staking 36 | func (c *stakingCaller) Create(candidateName string, amount *big.Int, duration uint32, autoStake bool) SendActionCaller { 37 | tx := iotextypes.StakeCreate{ 38 | CandidateName: candidateName, 39 | StakedDuration: duration, 40 | AutoStake: autoStake, 41 | StakedAmount: amount.String(), 42 | } 43 | c.action = &tx 44 | return c 45 | } 46 | 47 | //Unstake Staking 48 | func (c *stakingCaller) Unstake(bucketIndex uint64) SendActionCaller { 49 | tx := iotextypes.StakeReclaim{ 50 | BucketIndex: bucketIndex, 51 | } 52 | c.action = &reclaim{&tx, false} 53 | return c 54 | } 55 | 56 | //Withdraw Staking 57 | func (c *stakingCaller) Withdraw(bucketIndex uint64) SendActionCaller { 58 | tx := iotextypes.StakeReclaim{ 59 | BucketIndex: bucketIndex, 60 | } 61 | c.action = &reclaim{&tx, true} 62 | return c 63 | } 64 | 65 | //AddDeposit Staking 66 | func (c *stakingCaller) AddDeposit(index uint64, amount *big.Int) SendActionCaller { 67 | tx := iotextypes.StakeAddDeposit{ 68 | BucketIndex: index, 69 | Amount: amount.String(), 70 | } 71 | c.action = &tx 72 | return c 73 | } 74 | 75 | //ChangeCandidate Staking 76 | func (c *stakingCaller) ChangeCandidate(candName string, bucketIndex uint64) SendActionCaller { 77 | tx := iotextypes.StakeChangeCandidate{ 78 | CandidateName: candName, 79 | BucketIndex: bucketIndex, 80 | } 81 | c.action = &tx 82 | return c 83 | } 84 | 85 | //StakingTransfer Staking 86 | func (c *stakingCaller) StakingTransfer(voterAddress address.Address, bucketIndex uint64) SendActionCaller { 87 | tx := iotextypes.StakeTransferOwnership{ 88 | VoterAddress: voterAddress.String(), 89 | BucketIndex: bucketIndex, 90 | } 91 | c.action = &tx 92 | return c 93 | } 94 | 95 | //Restake Staking 96 | func (c *stakingCaller) Restake(index uint64, duration uint32, autoStake bool) SendActionCaller { 97 | tx := iotextypes.StakeRestake{ 98 | BucketIndex: index, 99 | StakedDuration: duration, 100 | AutoStake: autoStake, 101 | } 102 | c.action = &tx 103 | return c 104 | } 105 | 106 | //Register Staking 107 | func (c *stakingCaller) Register(name string, ownerAddr, operatorAddr, rewardAddr address.Address, amount *big.Int, duration uint32, autoStake bool, payload []byte) SendActionCaller { 108 | basic := iotextypes.CandidateBasicInfo{ 109 | Name: name, 110 | OperatorAddress: operatorAddr.String(), 111 | RewardAddress: rewardAddr.String(), 112 | } 113 | tx := iotextypes.CandidateRegister{ 114 | Candidate: &basic, 115 | StakedAmount: amount.String(), 116 | StakedDuration: duration, 117 | AutoStake: autoStake, 118 | OwnerAddress: ownerAddr.String(), 119 | Payload: payload, 120 | } 121 | c.action = &tx 122 | return c 123 | } 124 | 125 | //Update Staking 126 | func (c *stakingCaller) Update(name string, operatorAddr, rewardAddr address.Address) SendActionCaller { 127 | tx := iotextypes.CandidateBasicInfo{ 128 | Name: name, 129 | OperatorAddress: operatorAddr.String(), 130 | RewardAddress: rewardAddr.String(), 131 | } 132 | c.action = &tx 133 | return c 134 | } 135 | 136 | func (c *stakingCaller) SetGasLimit(g uint64) SendActionCaller { 137 | c.sendActionCaller.setGasLimit(g) 138 | return c 139 | } 140 | 141 | func (c *stakingCaller) SetGasPrice(g *big.Int) SendActionCaller { 142 | c.sendActionCaller.setGasPrice(g) 143 | return c 144 | } 145 | 146 | func (c *stakingCaller) SetNonce(n uint64) SendActionCaller { 147 | c.sendActionCaller.setNonce(n) 148 | return c 149 | } 150 | 151 | func (c *stakingCaller) SetPayload(pl []byte) SendActionCaller { 152 | c.sendActionCaller.setPayload(pl) 153 | return c 154 | } 155 | 156 | //Call call sendActionCaller 157 | func (c *stakingCaller) Call(ctx context.Context, opts ...grpc.CallOption) (hash.Hash256, error) { 158 | c.core = &iotextypes.ActionCore{ 159 | Version: ProtocolVersion, 160 | } 161 | 162 | hasPayload := len(c.payload) > 0 163 | switch a := c.action.(type) { 164 | case *iotextypes.StakeCreate: 165 | if hasPayload { 166 | a.Payload = c.payload 167 | } 168 | c.core.Action = &iotextypes.ActionCore_StakeCreate{StakeCreate: a} 169 | case *reclaim: 170 | if hasPayload { 171 | a.action.Payload = c.payload 172 | } 173 | if a.isWithdraw { 174 | c.core.Action = &iotextypes.ActionCore_StakeWithdraw{StakeWithdraw: a.action} 175 | } else { 176 | c.core.Action = &iotextypes.ActionCore_StakeUnstake{StakeUnstake: a.action} 177 | } 178 | case *iotextypes.StakeAddDeposit: 179 | if hasPayload { 180 | a.Payload = c.payload 181 | } 182 | c.core.Action = &iotextypes.ActionCore_StakeAddDeposit{StakeAddDeposit: a} 183 | case *iotextypes.StakeRestake: 184 | if hasPayload { 185 | a.Payload = c.payload 186 | } 187 | c.core.Action = &iotextypes.ActionCore_StakeRestake{StakeRestake: a} 188 | case *iotextypes.StakeChangeCandidate: 189 | if hasPayload { 190 | a.Payload = c.payload 191 | } 192 | c.core.Action = &iotextypes.ActionCore_StakeChangeCandidate{StakeChangeCandidate: a} 193 | case *iotextypes.StakeTransferOwnership: 194 | if hasPayload { 195 | a.Payload = c.payload 196 | } 197 | c.core.Action = &iotextypes.ActionCore_StakeTransferOwnership{StakeTransferOwnership: a} 198 | case *iotextypes.CandidateRegister: 199 | if hasPayload { 200 | a.Payload = c.payload 201 | } 202 | c.core.Action = &iotextypes.ActionCore_CandidateRegister{CandidateRegister: a} 203 | case *iotextypes.CandidateBasicInfo: 204 | c.core.Action = &iotextypes.ActionCore_CandidateUpdate{CandidateUpdate: a} 205 | default: 206 | return hash.ZeroHash256, errcodes.New("not support action call", errcodes.InternalError) 207 | } 208 | return c.sendActionCaller.Call(ctx, opts...) 209 | } 210 | -------------------------------------------------------------------------------- /iotex/caller_staking_test.go: -------------------------------------------------------------------------------- 1 | package iotex 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "math/big" 7 | "testing" 8 | 9 | "github.com/golang/mock/gomock" 10 | "github.com/iotexproject/go-pkgs/hash" 11 | "github.com/iotexproject/iotex-address/address" 12 | "github.com/stretchr/testify/require" 13 | 14 | "github.com/iotexproject/iotex-antenna-go/v2/utils/unit" 15 | ) 16 | 17 | var ( 18 | testActionHash1, _ = hash.HexStringToHash256("16fbdac39a19f433b32e457eba9c64c48d9fafcdffaddb11a0b7d264c0cf1418") 19 | // CandidateRegister tests. 20 | candidateRegisterTests = []struct { 21 | // input 22 | name string 23 | owner string 24 | operator string 25 | reward string 26 | amount *big.Int 27 | duration uint32 28 | autoStake bool 29 | gasPrice *big.Int 30 | gasLimit uint64 31 | payload []byte 32 | // expect 33 | actionHash hash.Hash256 34 | err error 35 | }{ 36 | { 37 | "1111", 38 | "io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he", 39 | "io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he", 40 | "io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he", 41 | big.NewInt(int64(unit.Iotx)), 42 | 10000, 43 | false, 44 | big.NewInt(int64(10 * unit.Qev)), 45 | 1000000, 46 | []byte("TestCandidateRegister"), 47 | testActionHash1, 48 | nil, 49 | }, 50 | // failure case 51 | { 52 | "2222", 53 | "00000000000000000000000000000000000000000", 54 | "00000000000000000000000000000000000000000", 55 | "00000000000000000000000000000000000000000", 56 | big.NewInt(0), 57 | 0, 58 | false, 59 | big.NewInt(int64(0)), 60 | 0, 61 | []byte(""), 62 | hash.Hash256{}, 63 | errors.New("address error"), 64 | }, 65 | } 66 | // Stake tests. 67 | stakeTests = []struct { 68 | candidateName string 69 | amount *big.Int 70 | duration uint32 71 | autoStake bool 72 | gasPrice *big.Int 73 | gasLimit uint64 74 | payload []byte 75 | // expect 76 | actionHash hash.Hash256 77 | err error 78 | }{ 79 | { 80 | "io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he", 81 | big.NewInt(int64(unit.Iotx)), 82 | 10000, 83 | false, 84 | big.NewInt(int64(10 * unit.Qev)), 85 | 1000000, 86 | []byte("TestStaking"), 87 | testActionHash1, 88 | nil, 89 | }, 90 | // failure case 91 | { 92 | "00000000000000000000000000000000000000000", 93 | big.NewInt(int64(unit.Iotx)), 94 | 10000, 95 | false, 96 | big.NewInt(int64(10 * unit.Qev)), 97 | 1000000, 98 | []byte("TestStaking"), 99 | hash.Hash256{}, 100 | errors.New("address error"), 101 | }, 102 | } 103 | // Unstake tests. 104 | unstakeTests = []struct { 105 | bucketIndex uint64 106 | autoStake bool 107 | gasPrice *big.Int 108 | gasLimit uint64 109 | payload []byte 110 | // expect 111 | actionHash hash.Hash256 112 | err error 113 | }{ 114 | { 115 | 10000, 116 | false, 117 | big.NewInt(int64(10 * unit.Qev)), 118 | 1000000, 119 | []byte("TestStaking"), 120 | testActionHash1, 121 | nil, 122 | }, 123 | // failure case 124 | { 125 | 0, 126 | false, 127 | big.NewInt(0), 128 | 0, 129 | []byte{}, 130 | hash.Hash256{}, 131 | errors.New("invalid gas price"), 132 | }, 133 | } 134 | ) 135 | 136 | func TestCandidateCaller_Register(t *testing.T) { 137 | require := require.New(t) 138 | 139 | ctrl := gomock.NewController(t) 140 | defer ctrl.Finish() 141 | 142 | testTimes := len(candidateRegisterTests) 143 | 144 | stakingAPICaller := NewMockSendActionCaller(ctrl) 145 | stakingAPICaller.EXPECT().SetGasPrice(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 146 | stakingAPICaller.EXPECT().SetGasLimit(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 147 | stakingAPICaller.EXPECT().SetPayload(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 148 | candidateCaller := NewMockCandidateCaller(ctrl) 149 | candidateCaller.EXPECT().Register(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(stakingAPICaller).Times(testTimes) 150 | client := NewMockAuthedClient(ctrl) 151 | client.EXPECT().Candidate().Return(candidateCaller).Times(testTimes) 152 | 153 | for _, test := range candidateRegisterTests { 154 | stakingAPICaller.EXPECT().Call(gomock.Any()).Return(test.actionHash, test.err).Times(1) 155 | ownerAddr, _ := address.FromString(test.owner) 156 | operatorAddr, _ := address.FromString(test.operator) 157 | rewardAddr, _ := address.FromString(test.reward) 158 | ret, err := client.Candidate(). 159 | Register(test.name, ownerAddr, operatorAddr, rewardAddr, test.amount, test.duration, test.autoStake, test.payload). 160 | SetGasPrice(test.gasPrice). 161 | SetGasLimit(test.gasLimit). 162 | SetPayload(test.payload). 163 | Call(context.Background()) 164 | require.Equal(test.err, err) 165 | require.Equal(test.actionHash, ret) 166 | } 167 | } 168 | 169 | func TestStakingCaller_Create(t *testing.T) { 170 | require := require.New(t) 171 | 172 | ctrl := gomock.NewController(t) 173 | defer ctrl.Finish() 174 | 175 | testTimes := len(stakeTests) 176 | 177 | stakingAPICaller := NewMockSendActionCaller(ctrl) 178 | stakingAPICaller.EXPECT().SetGasPrice(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 179 | stakingAPICaller.EXPECT().SetGasLimit(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 180 | stakingAPICaller.EXPECT().SetPayload(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 181 | stakingCaller := NewMockStakingCaller(ctrl) 182 | stakingCaller.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(stakingAPICaller).Times(testTimes) 183 | clt := NewMockAuthedClient(ctrl) 184 | clt.EXPECT().Staking().Return(stakingCaller).Times(testTimes) 185 | 186 | for _, test := range stakeTests { 187 | stakingAPICaller.EXPECT().Call(gomock.Any()).Return(test.actionHash, test.err).Times(1) 188 | ret, err := clt.Staking(). 189 | Create(test.candidateName, test.amount, test.duration, test.autoStake). 190 | SetGasPrice(test.gasPrice). 191 | SetGasLimit(test.gasLimit). 192 | SetPayload(test.payload). 193 | Call(context.Background()) 194 | require.Equal(test.err, err) 195 | require.Equal(test.actionHash, ret) 196 | } 197 | } 198 | 199 | func TestStakingCaller_Unstake(t *testing.T) { 200 | require := require.New(t) 201 | 202 | ctrl := gomock.NewController(t) 203 | defer ctrl.Finish() 204 | 205 | testTimes := len(unstakeTests) 206 | 207 | stakingAPICaller := NewMockSendActionCaller(ctrl) 208 | stakingAPICaller.EXPECT().SetGasPrice(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 209 | stakingAPICaller.EXPECT().SetGasLimit(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 210 | stakingAPICaller.EXPECT().SetPayload(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 211 | stakingCaller := NewMockStakingCaller(ctrl) 212 | stakingCaller.EXPECT().Unstake(gomock.Any()).Return(stakingAPICaller).Times(testTimes) 213 | clt := NewMockAuthedClient(ctrl) 214 | clt.EXPECT().Staking().Return(stakingCaller).Times(testTimes) 215 | 216 | for _, test := range unstakeTests { 217 | stakingAPICaller.EXPECT().Call(gomock.Any()).Return(test.actionHash, test.err).Times(1) 218 | ret, err := clt.Staking(). 219 | Unstake(test.bucketIndex). 220 | SetGasPrice(test.gasPrice). 221 | SetGasLimit(test.gasLimit). 222 | SetPayload(test.payload). 223 | Call(context.Background()) 224 | require.Equal(test.err, err) 225 | require.Equal(test.actionHash, ret) 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /iotex/caller_transfer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package iotex 8 | 9 | import ( 10 | "context" 11 | "math/big" 12 | 13 | "github.com/iotexproject/go-pkgs/hash" 14 | "github.com/iotexproject/iotex-address/address" 15 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 16 | "google.golang.org/grpc" 17 | 18 | "github.com/iotexproject/iotex-antenna-go/v2/errcodes" 19 | ) 20 | 21 | type transferCaller struct { 22 | *sendActionCaller 23 | amount *big.Int 24 | recipient address.Address 25 | } 26 | 27 | func (c *transferCaller) SetPayload(pl []byte) SendActionCaller { 28 | c.sendActionCaller.setPayload(pl) 29 | return c 30 | } 31 | 32 | func (c *transferCaller) SetGasLimit(g uint64) SendActionCaller { 33 | c.sendActionCaller.setGasLimit(g) 34 | return c 35 | } 36 | 37 | func (c *transferCaller) SetGasPrice(g *big.Int) SendActionCaller { 38 | c.sendActionCaller.setGasPrice(g) 39 | return c 40 | } 41 | 42 | func (c *transferCaller) SetNonce(n uint64) SendActionCaller { 43 | c.sendActionCaller.setNonce(n) 44 | return c 45 | } 46 | 47 | func (c *transferCaller) Call(ctx context.Context, opts ...grpc.CallOption) (hash.Hash256, error) { 48 | if c.amount == nil { 49 | return hash.ZeroHash256, errcodes.New("transfer amount cannot be nil", errcodes.InvalidParam) 50 | } 51 | 52 | tx := iotextypes.Transfer{ 53 | Amount: c.amount.String(), 54 | Recipient: c.recipient.String(), 55 | Payload: c.payload, 56 | } 57 | c.core = &iotextypes.ActionCore{ 58 | Version: ProtocolVersion, 59 | Action: &iotextypes.ActionCore_Transfer{Transfer: &tx}, 60 | } 61 | return c.sendActionCaller.Call(ctx, opts...) 62 | } 63 | -------------------------------------------------------------------------------- /iotex/callers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package iotex 8 | 9 | import ( 10 | "context" 11 | "encoding/hex" 12 | "math/big" 13 | 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/iotexproject/go-pkgs/hash" 16 | "github.com/iotexproject/iotex-address/address" 17 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 18 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 19 | "google.golang.org/grpc" 20 | 21 | "github.com/iotexproject/iotex-antenna-go/v2/account" 22 | "github.com/iotexproject/iotex-antenna-go/v2/errcodes" 23 | ) 24 | 25 | // ProtocolVersion is the iotex protocol version to use. Currently 1. 26 | const ProtocolVersion = 1 27 | 28 | type sendActionCaller struct { 29 | account account.Account 30 | api iotexapi.APIServiceClient 31 | nonce uint64 32 | gasLimit uint64 33 | gasPrice *big.Int 34 | chainID uint32 35 | payload []byte 36 | core *iotextypes.ActionCore 37 | } 38 | 39 | //API returns api 40 | func (c *sendActionCaller) API() iotexapi.APIServiceClient { 41 | return c.api 42 | } 43 | 44 | func (c *sendActionCaller) setNonce(n uint64) { 45 | c.nonce = n 46 | } 47 | 48 | func (c *sendActionCaller) setGasLimit(g uint64) { 49 | c.gasLimit = g 50 | } 51 | 52 | func (c *sendActionCaller) setGasPrice(g *big.Int) { 53 | c.gasPrice = g 54 | } 55 | 56 | func (c *sendActionCaller) setPayload(pl []byte) { 57 | if pl == nil { 58 | return 59 | } 60 | c.payload = make([]byte, len(pl)) 61 | copy(c.payload, pl) 62 | } 63 | 64 | func (c *sendActionCaller) Call(ctx context.Context, opts ...grpc.CallOption) (hash.Hash256, error) { 65 | if c.chainID == 0 { 66 | return hash.ZeroHash256, errcodes.New("0 is not a valid chain ID (use 1 for mainnet, 2 for testnet)", errcodes.InvalidParam) 67 | } 68 | c.core.ChainID = c.chainID 69 | 70 | if c.nonce == 0 { 71 | res, err := c.api.GetAccount(ctx, &iotexapi.GetAccountRequest{Address: c.account.Address().String()}, opts...) 72 | if err != nil { 73 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.RPCError) 74 | } 75 | c.nonce = res.GetAccountMeta().GetPendingNonce() 76 | } 77 | c.core.Nonce = c.nonce 78 | 79 | if c.gasLimit == 0 { 80 | sealed, err := sign(c.account, c.core) 81 | if err != nil { 82 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.InternalError) 83 | } 84 | request := &iotexapi.EstimateGasForActionRequest{Action: sealed} 85 | response, err := c.api.EstimateGasForAction(ctx, request, opts...) 86 | if err != nil { 87 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.RPCError) 88 | } 89 | c.gasLimit = response.GetGas() 90 | } 91 | c.core.GasLimit = c.gasLimit 92 | 93 | if c.gasPrice == nil { 94 | response, err := c.api.SuggestGasPrice(ctx, &iotexapi.SuggestGasPriceRequest{}, opts...) 95 | if err != nil { 96 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.RPCError) 97 | } 98 | c.gasPrice = big.NewInt(0).SetUint64(response.GetGasPrice()) 99 | } 100 | c.core.GasPrice = c.gasPrice.String() 101 | 102 | sealed, err := sign(c.account, c.core) 103 | if err != nil { 104 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.InternalError) 105 | } 106 | 107 | response, err := c.api.SendAction(ctx, &iotexapi.SendActionRequest{Action: sealed}, opts...) 108 | if err != nil { 109 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.RPCError) 110 | } 111 | h, err := hash.HexStringToHash256(response.GetActionHash()) 112 | if err != nil { 113 | return hash.ZeroHash256, errcodes.NewError(err, errcodes.BadResponse) 114 | } 115 | c.nonce = 0 // reset before next time use 116 | return h, nil 117 | } 118 | 119 | type getReceiptCaller struct { 120 | api iotexapi.APIServiceClient 121 | actionHash hash.Hash256 122 | } 123 | 124 | func (c *getReceiptCaller) Call(ctx context.Context, opts ...grpc.CallOption) (*iotexapi.GetReceiptByActionResponse, error) { 125 | h := hex.EncodeToString(c.actionHash[:]) 126 | return c.api.GetReceiptByAction(ctx, &iotexapi.GetReceiptByActionRequest{ActionHash: h}, opts...) 127 | } 128 | 129 | type getLogsCaller struct { 130 | api iotexapi.APIServiceClient 131 | Request *iotexapi.GetLogsRequest 132 | } 133 | 134 | func (c *getLogsCaller) Call(ctx context.Context, opts ...grpc.CallOption) (*iotexapi.GetLogsResponse, error) { 135 | return c.api.GetLogs(ctx, c.Request, opts...) 136 | } 137 | 138 | func addressTypeAssert(preVal interface{}) (common.Address, error) { 139 | switch v := preVal.(type) { 140 | case string: 141 | ioAddress, err := address.FromString(v) 142 | if err != nil { 143 | return common.Address{}, errcodes.New("fail to convert string to ioAddress", errcodes.InvalidParam) 144 | } 145 | return common.HexToAddress(hex.EncodeToString(ioAddress.Bytes())), nil 146 | case address.Address: 147 | return common.HexToAddress(hex.EncodeToString(v.Bytes())), nil 148 | case common.Address: 149 | return v, nil 150 | default: 151 | return common.Address{}, errcodes.New("fail to convert from interface to string/ioAddress/ethAddress", errcodes.InvalidParam) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /iotex/client.go: -------------------------------------------------------------------------------- 1 | package iotex 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/accounts/abi" 7 | "github.com/iotexproject/go-pkgs/hash" 8 | "github.com/iotexproject/iotex-address/address" 9 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 10 | 11 | "github.com/iotexproject/iotex-antenna-go/v2/account" 12 | ) 13 | 14 | type authedClient struct { 15 | client 16 | chainID uint32 17 | account account.Account 18 | } 19 | 20 | // NewAuthedClient creates an AuthedClient using given account's credentials. 21 | func NewAuthedClient(api iotexapi.APIServiceClient, chainID uint32, a account.Account) AuthedClient { 22 | return &authedClient{ 23 | client: client{ 24 | api: api, 25 | }, 26 | chainID: chainID, 27 | account: a, 28 | } 29 | } 30 | 31 | func (c *authedClient) Contract(co address.Address, abi abi.ABI) Contract { 32 | return &contract{ 33 | sendActionCaller: &sendActionCaller{ 34 | chainID: c.chainID, 35 | account: c.account, 36 | api: c.api, 37 | }, 38 | address: co, 39 | abi: &abi, 40 | } 41 | } 42 | 43 | func (c *authedClient) Transfer(to address.Address, value *big.Int) SendActionCaller { 44 | return &transferCaller{ 45 | sendActionCaller: &sendActionCaller{ 46 | chainID: c.chainID, 47 | account: c.account, 48 | api: c.api, 49 | }, 50 | amount: value, 51 | recipient: to, 52 | } 53 | } 54 | 55 | func (c *authedClient) ClaimReward(value *big.Int) ClaimRewardCaller { 56 | return &claimRewardCaller{ 57 | sendActionCaller: &sendActionCaller{ 58 | chainID: c.chainID, 59 | account: c.account, 60 | api: c.api, 61 | }, 62 | amount: value, 63 | } 64 | } 65 | 66 | func (c *authedClient) DeployContract(data []byte) DeployContractCaller { 67 | return &deployContractCaller{ 68 | sendActionCaller: &sendActionCaller{ 69 | chainID: c.chainID, 70 | account: c.account, 71 | api: c.api, 72 | payload: data, 73 | }, 74 | } 75 | } 76 | 77 | //Staking interface 78 | func (c *authedClient) Staking() StakingCaller { 79 | return &stakingCaller{ 80 | sendActionCaller: &sendActionCaller{ 81 | chainID: c.chainID, 82 | account: c.account, 83 | api: c.api, 84 | }} 85 | } 86 | 87 | //Candidate interface 88 | func (c *authedClient) Candidate() CandidateCaller { 89 | return &stakingCaller{ 90 | sendActionCaller: &sendActionCaller{ 91 | chainID: c.chainID, 92 | account: c.account, 93 | api: c.api, 94 | }} 95 | } 96 | 97 | func (c *authedClient) Account() account.Account { return c.account } 98 | 99 | func (c *authedClient) ChainID() uint32 { return c.chainID } 100 | 101 | // NewReadOnlyClient creates a ReadOnlyClient. 102 | func NewReadOnlyClient(c iotexapi.APIServiceClient) ReadOnlyClient { 103 | return &client{api: c} 104 | } 105 | 106 | type client struct { 107 | api iotexapi.APIServiceClient 108 | } 109 | 110 | func (c *client) ReadOnlyContract(contract address.Address, abi abi.ABI) ReadOnlyContract { 111 | return &readOnlyContract{ 112 | address: contract, 113 | abi: &abi, 114 | api: c.api, 115 | } 116 | } 117 | 118 | func (c *client) GetReceipt(actionHash hash.Hash256) GetReceiptCaller { 119 | return &getReceiptCaller{ 120 | api: c.api, 121 | actionHash: actionHash, 122 | } 123 | } 124 | 125 | func (c *client) GetLogs(request *iotexapi.GetLogsRequest) GetLogsCaller { 126 | return &getLogsCaller{ 127 | api: c.api, 128 | Request: request, 129 | } 130 | } 131 | 132 | func (c *client) API() iotexapi.APIServiceClient { return c.api } 133 | -------------------------------------------------------------------------------- /iotex/contract.go: -------------------------------------------------------------------------------- 1 | package iotex 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/accounts/abi" 5 | "github.com/iotexproject/iotex-address/address" 6 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 7 | ) 8 | 9 | // Data is the data returned from read contract. 10 | type Data struct { 11 | method string 12 | abi *abi.ABI 13 | Raw []byte 14 | } 15 | 16 | // Unmarshal unmarshals data into a data holder object. 17 | func (d Data) Unmarshal() ([]interface{}, error) { return d.abi.Unpack(d.method, d.Raw) } 18 | 19 | type contract struct { 20 | *sendActionCaller 21 | address address.Address 22 | abi *abi.ABI 23 | } 24 | 25 | func (c *contract) Read(method string, args ...interface{}) ReadContractCaller { 26 | return &readContractCaller{ 27 | api: c.api, 28 | contractArgs: contractArgs{ 29 | contract: c.address, 30 | abi: c.abi, 31 | method: method, 32 | args: args, 33 | }, 34 | } 35 | } 36 | 37 | func (c *contract) Execute(method string, args ...interface{}) ExecuteContractCaller { 38 | return &executeContractCaller{ 39 | sendActionCaller: c.sendActionCaller, 40 | contractArgs: contractArgs{ 41 | contract: c.address, 42 | abi: c.abi, 43 | method: method, 44 | args: args, 45 | }, 46 | } 47 | } 48 | 49 | type readOnlyContract struct { 50 | address address.Address 51 | abi *abi.ABI 52 | api iotexapi.APIServiceClient 53 | } 54 | 55 | func (c *readOnlyContract) Read(method string, args ...interface{}) ReadContractCaller { 56 | return &readContractCaller{ 57 | api: c.api, 58 | contractArgs: contractArgs{ 59 | contract: c.address, 60 | abi: c.abi, 61 | method: method, 62 | args: args, 63 | }, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /iotex/default.go: -------------------------------------------------------------------------------- 1 | package iotex 2 | 3 | import ( 4 | "crypto/tls" 5 | "time" 6 | 7 | grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/credentials" 10 | ) 11 | 12 | // NewDefaultGRPCConn creates a default grpc connection. With tls and retry. 13 | func NewDefaultGRPCConn(endpoint string) (*grpc.ClientConn, error) { 14 | opts := []grpc_retry.CallOption{ 15 | grpc_retry.WithBackoff(grpc_retry.BackoffLinear(100 * time.Second)), 16 | grpc_retry.WithMax(3), 17 | } 18 | return grpc.Dial(endpoint, 19 | grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(opts...)), 20 | grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(opts...)), 21 | grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) 22 | } 23 | 24 | // NewGRPCConnWithoutTLS creates a default grpc connection, without tls 25 | func NewGRPCConnWithoutTLS(endpoint string) (*grpc.ClientConn, error) { 26 | opts := []grpc_retry.CallOption{ 27 | grpc_retry.WithBackoff(grpc_retry.BackoffLinear(100 * time.Second)), 28 | grpc_retry.WithMax(3), 29 | } 30 | return grpc.Dial(endpoint, 31 | grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(opts...)), 32 | grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(opts...)), 33 | grpc.WithInsecure()) 34 | } 35 | -------------------------------------------------------------------------------- /iotex/interfaces.go: -------------------------------------------------------------------------------- 1 | package iotex 2 | 3 | import ( 4 | "context" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/accounts/abi" 8 | "github.com/iotexproject/go-pkgs/hash" 9 | "github.com/iotexproject/iotex-address/address" 10 | "github.com/iotexproject/iotex-antenna-go/v2/account" 11 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | // Caller is used to perform a send action call. 16 | type Caller interface { 17 | API() iotexapi.APIServiceClient 18 | Call(ctx context.Context, opts ...grpc.CallOption) (hash.Hash256, error) 19 | } 20 | 21 | // SendActionCaller is used to set nonce/gas etc. on top of Caller 22 | type SendActionCaller interface { 23 | Caller 24 | SetNonce(uint64) SendActionCaller 25 | SetGasLimit(uint64) SendActionCaller 26 | SetGasPrice(*big.Int) SendActionCaller 27 | SetPayload([]byte) SendActionCaller 28 | } 29 | 30 | // ClaimRewardCaller is used to perform a claim reward call. 31 | type ClaimRewardCaller interface { 32 | Caller 33 | 34 | SetGasPrice(*big.Int) ClaimRewardCaller 35 | SetGasLimit(uint64) ClaimRewardCaller 36 | SetData([]byte) ClaimRewardCaller 37 | SetNonce(uint64) ClaimRewardCaller 38 | } 39 | 40 | // GetReceiptCaller is used to perform a get receipt call. 41 | type GetReceiptCaller interface { 42 | Call(ctx context.Context, opts ...grpc.CallOption) (*iotexapi.GetReceiptByActionResponse, error) 43 | } 44 | 45 | // GetLogsCaller is used to get logs 46 | type GetLogsCaller interface { 47 | Call(ctx context.Context, opts ...grpc.CallOption) (*iotexapi.GetLogsResponse, error) 48 | } 49 | 50 | // AuthedClient is an iotex client which associate with an account credentials, so it can perform write actions. 51 | type AuthedClient interface { 52 | ReadOnlyClient 53 | 54 | Contract(contract address.Address, abi abi.ABI) Contract 55 | Transfer(to address.Address, value *big.Int) SendActionCaller 56 | ClaimReward(value *big.Int) ClaimRewardCaller 57 | DeployContract(data []byte) DeployContractCaller 58 | // staking related 59 | Staking() StakingCaller 60 | Candidate() CandidateCaller 61 | Account() account.Account 62 | ChainID() uint32 63 | } 64 | 65 | // ReadOnlyClient is an iotex client which can perform read actions. 66 | type ReadOnlyClient interface { 67 | ReadOnlyContract(contract address.Address, abi abi.ABI) ReadOnlyContract 68 | GetReceipt(actionHash hash.Hash256) GetReceiptCaller 69 | GetLogs(request *iotexapi.GetLogsRequest) GetLogsCaller 70 | API() iotexapi.APIServiceClient 71 | } 72 | 73 | // ReadContractCaller is used to perform a read contract call. 74 | type ReadContractCaller interface { 75 | Call(ctx context.Context, opts ...grpc.CallOption) (Data, error) 76 | } 77 | 78 | // ExecuteContractCaller is used to perform an execute contract call. 79 | type ExecuteContractCaller interface { 80 | Caller 81 | 82 | SetGasPrice(*big.Int) ExecuteContractCaller 83 | SetGasLimit(uint64) ExecuteContractCaller 84 | SetAmount(*big.Int) ExecuteContractCaller 85 | SetNonce(uint64) ExecuteContractCaller 86 | } 87 | 88 | // DeployContractCaller is used to perform a deploy contract call. 89 | type DeployContractCaller interface { 90 | Caller 91 | 92 | SetArgs(abi abi.ABI, args ...interface{}) DeployContractCaller 93 | SetGasPrice(*big.Int) DeployContractCaller 94 | SetGasLimit(uint64) DeployContractCaller 95 | SetNonce(uint64) DeployContractCaller 96 | } 97 | 98 | // Contract allows to read or execute on this contract's methods. 99 | type Contract interface { 100 | ReadOnlyContract 101 | 102 | Execute(method string, args ...interface{}) ExecuteContractCaller 103 | } 104 | 105 | // ReadOnlyContract allows to read on this contract's methods. 106 | type ReadOnlyContract interface { 107 | Read(method string, args ...interface{}) ReadContractCaller 108 | } 109 | 110 | // StakingCaller is used to perform a staking call. 111 | type StakingCaller interface { 112 | Create(candidateName string, amount *big.Int, duration uint32, autoStake bool) SendActionCaller 113 | Unstake(bucketIndex uint64) SendActionCaller 114 | Withdraw(bucketIndex uint64) SendActionCaller 115 | AddDeposit(index uint64, amount *big.Int) SendActionCaller 116 | ChangeCandidate(candName string, bucketIndex uint64) SendActionCaller 117 | StakingTransfer(voterAddress address.Address, bucketIndex uint64) SendActionCaller 118 | Restake(index uint64, duration uint32, autoStake bool) SendActionCaller 119 | } 120 | 121 | // CandidateCaller is used to perform a candidate call. 122 | type CandidateCaller interface { 123 | Register(name string, ownerAddr, operatorAddr, rewardAddr address.Address, amount *big.Int, duration uint32, autoStake bool, payload []byte) SendActionCaller 124 | Update(name string, operatorAddr, rewardAddr address.Address) SendActionCaller 125 | } 126 | -------------------------------------------------------------------------------- /iotex/iotex_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package iotex 8 | 9 | import ( 10 | "context" 11 | "encoding/hex" 12 | "math/big" 13 | "strings" 14 | "testing" 15 | 16 | "github.com/ethereum/go-ethereum/accounts/abi" 17 | "github.com/iotexproject/go-pkgs/hash" 18 | "github.com/iotexproject/iotex-address/address" 19 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 20 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 21 | "github.com/stretchr/testify/require" 22 | "google.golang.org/grpc" 23 | "google.golang.org/protobuf/proto" 24 | 25 | "github.com/iotexproject/iotex-antenna-go/v2/account" 26 | "github.com/iotexproject/iotex-antenna-go/v2/utils/unit" 27 | ) 28 | 29 | const ( 30 | _testnet = "api.testnet.iotex.one:443" 31 | _mainnet = "api.iotex.one:443" 32 | _accountPrivateKey = "73c7b4a62bf165dccf8ebdea8278db811efd5b8638e2ed9683d2d94889450426" 33 | _to = "io1emxf8zzqckhgjde6dqd97ts0y3q496gm3fdrl6" 34 | _testNetChainID = 4690 35 | ) 36 | 37 | func TestTransfer(t *testing.T) { 38 | require := require.New(t) 39 | conn, err := NewDefaultGRPCConn(_testnet) 40 | require.NoError(err) 41 | defer conn.Close() 42 | 43 | acc, err := account.HexStringToAccount(_accountPrivateKey) 44 | require.NoError(err) 45 | 46 | to, err := address.FromString(_to) 47 | require.NoError(err) 48 | v := big.NewInt(160000000000000000) 49 | for _, test := range []struct { 50 | chainID uint32 51 | err string 52 | }{ 53 | {0, "0 is not a valid chain ID (use 1 for mainnet, 2 for testnet)"}, 54 | {1, "ChainID does not match, expecting 2, got 1"}, 55 | {2, ""}, 56 | } { 57 | c := NewAuthedClient(iotexapi.NewAPIServiceClient(conn), test.chainID, acc) 58 | caller := c.Transfer(to, v) 59 | hash, err := caller.SetGasPrice(big.NewInt(int64(unit.Qev))).SetGasLimit(1000000).Call(context.Background()) 60 | if len(test.err) > 0 { 61 | require.Contains(err.Error(), test.err) 62 | } else { 63 | require.NoError(err) 64 | require.NotEmpty(hash) 65 | } 66 | } 67 | } 68 | 69 | func TestStake(t *testing.T) { 70 | require := require.New(t) 71 | conn, err := NewDefaultGRPCConn(_testnet) 72 | require.NoError(err) 73 | defer conn.Close() 74 | 75 | acc, err := account.HexStringToAccount("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5") 76 | require.NoError(err) 77 | c := NewAuthedClient(iotexapi.NewAPIServiceClient(conn), 2, acc) 78 | 79 | one := big.NewInt(int64(unit.Iotx)) 80 | _, err = c.Staking().Create("robotbp00001", one.Lsh(one, 7), 0, false). 81 | SetGasPrice(big.NewInt(int64(unit.Qev))).SetGasLimit(20000).Call(context.Background()) 82 | require.Contains(err.Error(), "insufficient funds for gas * price + value") 83 | } 84 | 85 | func TestClaimReward(t *testing.T) { 86 | require := require.New(t) 87 | conn, err := NewDefaultGRPCConn(_testnet) 88 | require.NoError(err) 89 | defer conn.Close() 90 | 91 | acc, err := account.HexStringToAccount(_accountPrivateKey) 92 | require.NoError(err) 93 | c := NewAuthedClient(iotexapi.NewAPIServiceClient(conn), 2, acc) 94 | 95 | require.NoError(err) 96 | v := big.NewInt(1000000000000000000) 97 | hash, err := c.ClaimReward(v).SetGasPrice(big.NewInt(int64(unit.Qev))).SetGasLimit(1000000).Call(context.Background()) 98 | require.NoError(err) 99 | require.NotEmpty(hash) 100 | } 101 | 102 | func TestDeployContract(t *testing.T) { 103 | require := require.New(t) 104 | conn, err := NewDefaultGRPCConn(_testnet) 105 | require.NoError(err) 106 | defer conn.Close() 107 | 108 | acc, err := account.HexStringToAccount(_accountPrivateKey) 109 | require.NoError(err) 110 | c := NewAuthedClient(iotexapi.NewAPIServiceClient(conn), 2, acc) 111 | 112 | abi, err := abi.JSON(strings.NewReader(`[{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_x","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]`)) 113 | require.NoError(err) 114 | data, err := hex.DecodeString("608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058208d4f6c9737f34d9b28ef070baa8127c0876757fbf6f3945a7ea8d4387ca156590029") 115 | require.NoError(err) 116 | 117 | hash, err := c.DeployContract(data).SetGasPrice(big.NewInt(int64(unit.Qev))).SetGasLimit(1000000).SetArgs(abi, big.NewInt(10)).Call(context.Background()) 118 | require.NoError(err) 119 | require.NotNil(hash) 120 | } 121 | 122 | func TestExecuteContract(t *testing.T) { 123 | require := require.New(t) 124 | conn, err := NewDefaultGRPCConn(_testnet) 125 | require.NoError(err) 126 | defer conn.Close() 127 | 128 | acc, err := account.HexStringToAccount(_accountPrivateKey) 129 | require.NoError(err) 130 | c := NewAuthedClient(iotexapi.NewAPIServiceClient(conn), 2, acc) 131 | abi, err := abi.JSON(strings.NewReader(`[{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_x","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]`)) 132 | require.NoError(err) 133 | contract, err := address.FromString("io17sn486alutrnzlrdz9vv44g7qyc38hygf7s6h0") 134 | require.NoError(err) 135 | 136 | hash, err := c.Contract(contract, abi).Execute("set", big.NewInt(8)).SetGasPrice(big.NewInt(int64(unit.Qev))).SetGasLimit(1000000).Call(context.Background()) 137 | require.NoError(err) 138 | require.NotNil(hash) 139 | } 140 | 141 | func TestExecuteContractWithAddressArgument(t *testing.T) { 142 | require := require.New(t) 143 | conn, err := NewDefaultGRPCConn(_testnet) 144 | require.NoError(err) 145 | defer conn.Close() 146 | 147 | acc, err := account.HexStringToAccount(_accountPrivateKey) 148 | require.NoError(err) 149 | c := NewAuthedClient(iotexapi.NewAPIServiceClient(conn), 2, acc) 150 | abi, err := abi.JSON(strings.NewReader(`[ 151 | { 152 | "constant": false, 153 | "inputs": [ 154 | { 155 | "name": "recipients", 156 | "type": "address[]" 157 | }, 158 | { 159 | "name": "amounts", 160 | "type": "uint256[]" 161 | }, 162 | { 163 | "name": "payload", 164 | "type": "string" 165 | } 166 | ], 167 | "name": "multiSend", 168 | "outputs": [], 169 | "payable": true, 170 | "stateMutability": "payable", 171 | "type": "function" 172 | }, 173 | { 174 | "anonymous": false, 175 | "inputs": [ 176 | { 177 | "indexed": false, 178 | "name": "recipient", 179 | "type": "address" 180 | }, 181 | { 182 | "indexed": false, 183 | "name": "amount", 184 | "type": "uint256" 185 | } 186 | ], 187 | "name": "Transfer", 188 | "type": "event" 189 | }, 190 | { 191 | "anonymous": false, 192 | "inputs": [ 193 | { 194 | "indexed": false, 195 | "name": "refund", 196 | "type": "uint256" 197 | } 198 | ], 199 | "name": "Refund", 200 | "type": "event" 201 | }, 202 | { 203 | "anonymous": false, 204 | "inputs": [ 205 | { 206 | "indexed": false, 207 | "name": "payload", 208 | "type": "string" 209 | } 210 | ], 211 | "name": "Payload", 212 | "type": "event" 213 | } 214 | ]`)) 215 | require.NoError(err) 216 | contract, err := address.FromString("io1up8gd9nxhc0k0fjff7nrl6jn626vkdzj7y3g09") 217 | require.NoError(err) 218 | 219 | recipient1, err := address.FromString("io18jaldgzc8wlyfnzamgas62yu3kg5nw527czg37") 220 | require.NoError(err) 221 | recipient2, err := address.FromString("io1ntprz4p5zw38fvtfrcczjtcv3rkr3nqs6sm3pj") 222 | require.NoError(err) 223 | 224 | recipients := [2]address.Address{recipient1, recipient2} 225 | // recipients := [2]string{"io18jaldgzc8wlyfnzamgas62yu3kg5nw527czg37", "io1ntprz4p5zw38fvtfrcczjtcv3rkr3nqs6sm3pj"} 226 | amounts := [2]*big.Int{big.NewInt(1), big.NewInt(2)} 227 | actionHash, err := c.Contract(contract, abi).Execute("multiSend", recipients, amounts, "payload").SetGasPrice(big.NewInt(1000000000000)).SetGasLimit(1000000).Call(context.Background()) 228 | require.NoError(err) 229 | require.NotNil(actionHash) 230 | } 231 | 232 | func TestReadContract(t *testing.T) { 233 | require := require.New(t) 234 | conn, err := NewDefaultGRPCConn(_testnet) 235 | require.NoError(err) 236 | defer conn.Close() 237 | 238 | c := NewReadOnlyClient(iotexapi.NewAPIServiceClient(conn)) 239 | 240 | abi, err := abi.JSON(strings.NewReader(`[{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_x","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]`)) 241 | require.NoError(err) 242 | contract, err := address.FromString("io17sn486alutrnzlrdz9vv44g7qyc38hygf7s6h0") 243 | require.NoError(err) 244 | 245 | _, err = c.ReadOnlyContract(contract, abi).Read("get").Call(context.Background()) 246 | require.NoError(err) 247 | } 248 | 249 | func TestGetReceipt(t *testing.T) { 250 | require := require.New(t) 251 | conn, err := NewDefaultGRPCConn(_testnet) 252 | require.NoError(err) 253 | defer conn.Close() 254 | 255 | c := NewReadOnlyClient(iotexapi.NewAPIServiceClient(conn)) 256 | 257 | actionHash, err := hash.HexStringToHash256("163ece70353acfe8fa7929e756d96b1b3cfec1246bc5a8f397ca77f20a0d5c5f") 258 | require.NoError(err) 259 | _, err = c.GetReceipt(actionHash).Call(context.Background()) 260 | require.NoError(err) 261 | } 262 | 263 | func TestGetRlpTx(t *testing.T) { 264 | require := require.New(t) 265 | conn, err := NewDefaultGRPCConn(_testnet) 266 | require.NoError(err) 267 | defer conn.Close() 268 | 269 | c := iotexapi.NewAPIServiceClient(conn) 270 | ctx := context.Background() 271 | req := iotexapi.GetRawBlocksRequest{ 272 | Count: 1, 273 | WithTransactionLogs: true, 274 | } 275 | for _, v := range []struct { 276 | height uint64 277 | hash string 278 | }{ 279 | {8638208, "bc34eaac7fc9a4e3edc78aff514fa5631f722702ed35b4f696229d5da3f7e914"}, 280 | {8638638, "a43e8ee6c444da9b7524663afdde84dd3c4976f0127a3fbc3129e18480213386"}, 281 | {8638658, "76c2b74ea767529a2ecc45721e968b9a75c930be91742ac84dd200375af5ab76"}, 282 | } { 283 | req.StartHeight = v.height 284 | res, err := c.GetRawBlocks(ctx, &req) 285 | require.NoError(err) 286 | require.Equal(1, len(res.Blocks)) 287 | txLog := res.Blocks[0].TransactionLogs 288 | require.Equal(1, len(txLog.Logs)) 289 | log := txLog.Logs[0] 290 | require.Equal(v.hash, hex.EncodeToString(log.ActionHash)) 291 | require.Equal(2, len(log.Transactions)) 292 | for i, tx := range log.Transactions { 293 | if i == 0 { 294 | // first log is for native transfer 295 | require.Equal( 296 | iotextypes.TransactionLogType_name[int32(iotextypes.TransactionLogType_NATIVE_TRANSFER)], 297 | tx.Type.String()) 298 | } else { 299 | // second log is for gas fee in the amount of 0.01 IOTX 300 | require.Equal( 301 | iotextypes.TransactionLogType_name[int32(iotextypes.TransactionLogType_GAS_FEE)], 302 | tx.Type.String()) 303 | require.Equal("10000000000000000", tx.Amount) 304 | } 305 | } 306 | 307 | // verify hash 308 | act := res.Blocks[0].GetBlock().GetBody().GetActions()[0] 309 | require.Equal(act.Encoding, iotextypes.Encoding_ETHEREUM_RLP) 310 | h, err := ActionHash(act, _testNetChainID) 311 | require.NoError(err) 312 | require.Equal(h[:], log.ActionHash) 313 | } 314 | } 315 | 316 | func TestGetLogs(t *testing.T) { 317 | require := require.New(t) 318 | conn, err := NewDefaultGRPCConn(_testnet) 319 | require.NoError(err) 320 | defer conn.Close() 321 | 322 | c := NewReadOnlyClient(iotexapi.NewAPIServiceClient(conn)) 323 | 324 | // https://testnet.iotexscan.io/action/22cd0c2d1f7d65298cec7599e2d0e3c650dd8b4ed2b1c816d909026c60d785b2 325 | name, _ := hex.DecodeString("000000000000000000000000000000000000007472616e736665725374616b65") 326 | index, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000039") 327 | receiver, _ := hex.DecodeString("000000000000000000000000cb68a8318de4d4061e0956de69927c327bcfb352") 328 | sender, _ := hex.DecodeString("00000000000000000000000053fbc28faf9a52dfe5f591948a23189e900381b5") 329 | filterTopics := [][]byte{name, index, receiver, sender} 330 | blkHash, _ := hex.DecodeString("b13199e4cc712b3fee4feda52e39cec664ef5cbbc775ee1a66535305ff3a1af7") 331 | 332 | req := &iotexapi.GetLogsRequest{ 333 | Filter: &iotexapi.LogsFilter{ 334 | Address: []string{"io1qnpz47hx5q6r3w876axtrn6yz95d70cjl35r53"}, 335 | Topics: []*iotexapi.Topics{ 336 | &iotexapi.Topics{}, 337 | &iotexapi.Topics{Topic: filterTopics}, 338 | }, 339 | }, 340 | Lookup: &iotexapi.GetLogsRequest_ByBlock{ 341 | ByBlock: &iotexapi.GetLogsByBlock{ 342 | BlockHash: blkHash, 343 | }, 344 | }, 345 | } 346 | logs, err := c.GetLogs(req).Call(context.Background()) 347 | require.NoError(err) 348 | require.Equal(1, len(logs.Logs)) 349 | log := logs.Logs[0] 350 | for i := range filterTopics { 351 | require.Equal(filterTopics[i], log.Topics[i]) 352 | } 353 | 354 | // pulling with second topic (bucket ID) = 0x31 in 500 blocks 355 | index, _ = hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000031") 356 | req.Filter.Topics = []*iotexapi.Topics{ 357 | &iotexapi.Topics{}, 358 | &iotexapi.Topics{Topic: [][]byte{index}}, 359 | } 360 | req.Lookup = &iotexapi.GetLogsRequest_ByRange{ 361 | ByRange: &iotexapi.GetLogsByRange{ 362 | FromBlock: 4795567, 363 | ToBlock: 4796567, 364 | }, 365 | } 366 | logs, err = c.GetLogs(req).Call(context.Background()) 367 | require.NoError(err) 368 | require.Equal(5, len(logs.Logs)) 369 | 370 | // verify index == 0x31 371 | for _, log := range logs.Logs { 372 | require.True(len(log.Topics) >= 2) 373 | require.Equal(index, log.Topics[1]) 374 | } 375 | } 376 | 377 | // mainnet tests 378 | func mainnetGrpcConn() (*grpc.ClientConn, error) { 379 | return NewDefaultGRPCConn(_mainnet) 380 | } 381 | 382 | func TestMainnetGetActions(t *testing.T) { 383 | require := require.New(t) 384 | conn, err := mainnetGrpcConn() 385 | require.NoError(err) 386 | defer conn.Close() 387 | 388 | ctx := context.Background() 389 | c := NewReadOnlyClient(iotexapi.NewAPIServiceClient(conn)) 390 | res, err := c.API().GetActions(ctx, &iotexapi.GetActionsRequest{ 391 | Lookup: &iotexapi.GetActionsRequest_ByHash{ 392 | ByHash: &iotexapi.GetActionByHashRequest{ 393 | ActionHash: "84755e8be7ee7ba7891a57cf03b3c3dc65d2699b8935552704b900c70f38d4af", 394 | }, 395 | }, 396 | }) 397 | require.NoError(err) 398 | require.Equal(1, len(res.ActionInfo)) 399 | act := res.ActionInfo[0] 400 | require.Equal("84755e8be7ee7ba7891a57cf03b3c3dc65d2699b8935552704b900c70f38d4af", act.ActHash) 401 | require.Equal("7e42c615c7dde2297c98220520eb68b0af9d1e6ff856b793974a311aed8221b5", act.BlkHash) 402 | require.EqualValues(8007, act.BlkHeight) 403 | require.Equal("io17ch0jth3dxqa7w9vu05yu86mqh0n6502d92lmp", act.Sender) 404 | require.Equal("11493000000000000", act.GasFee) 405 | 406 | res, err = c.API().GetActions(ctx, &iotexapi.GetActionsRequest{ 407 | Lookup: &iotexapi.GetActionsRequest_ByIndex{ 408 | ByIndex: &iotexapi.GetActionsByIndexRequest{ 409 | Start: 0, 410 | Count: 5, 411 | }, 412 | }, 413 | }) 414 | require.NoError(err) 415 | require.Equal(5, len(res.ActionInfo)) 416 | for i, v := range []struct { 417 | height uint64 418 | actHash, blkHash, sender, gas string 419 | }{ 420 | {1, "dd2e83336f1ff219b1e54558f0627e1f556ed2caeedb44b758b0e107aa246531", "230ba8095d5a505e355652f9dcc2b13605419a8fa3d3fd5ddc6d24fd6a902641", "io1vtm2zgn830pn6auc2cvnchgwdaefa9gr4z0s86", "0"}, 421 | {1, "b7024bc52f315fafb9cc7677e730aec79767b28fbaa6bdd1f37c1861dd699aba", "230ba8095d5a505e355652f9dcc2b13605419a8fa3d3fd5ddc6d24fd6a902641", "io1vtm2zgn830pn6auc2cvnchgwdaefa9gr4z0s86", "0"}, 422 | {2, "833de10c30ffbedd898ae8669123362bdd1f4012ac0b979d784f201364d4dda0", "e6bdc2fd1d36f47ec8f9c5554503855eb29453a7cd4138b3ceb1af670fee6d75", "io1jafqlvntcxgyp6e0uxctt3tljzc3vyv5hg4ukh", "0"}, 423 | {3, "dbbd4c606b7743be3e064aa84e78e70dc786d1599c9bf37c03681dc8038b761e", "cb6055d916355600471f1ca2cf15098d56c7f59cff3dae0123228c8a2da8c1f2", "io1v0q5g2f8z6l3v25krl677chdx7g5pwt9kvqfpc", "0"}, 424 | {4, "ddd8149259ae3f169cf0f84ae8bb420089860a9ce2cb7a79c28edd475e002ae8", "67b9de1ffda9995c033e37f2d3dabdfab798aa240908f02dc8096037f12613da", "io1nyjs526mnqcsx4twa7nptkg08eclsw5c2dywp4", "0"}, 425 | } { 426 | act := res.ActionInfo[i] 427 | require.Equal(v.actHash, act.ActHash) 428 | require.Equal(v.blkHash, act.BlkHash) 429 | require.Equal(v.height, act.BlkHeight) 430 | require.Equal(v.sender, act.Sender) 431 | require.Equal(v.gas, act.GasFee) 432 | } 433 | 434 | res, err = c.API().GetActions(ctx, &iotexapi.GetActionsRequest{ 435 | Lookup: &iotexapi.GetActionsRequest_ByBlk{ 436 | ByBlk: &iotexapi.GetActionsByBlockRequest{ 437 | BlkHash: "e222dfbd059e064369b65f6553455329a1640810c88776154c8db08071b8c860", 438 | Start: 0, 439 | Count: 4, 440 | }, 441 | }, 442 | }) 443 | require.NoError(err) 444 | require.Equal(3, len(res.ActionInfo)) 445 | for i, v := range []string{ 446 | "70714000000000000", 447 | "304233000000000000", 448 | "0", 449 | } { 450 | act = res.ActionInfo[i] 451 | require.Equal("e222dfbd059e064369b65f6553455329a1640810c88776154c8db08071b8c860", act.BlkHash) 452 | require.EqualValues(14527394, act.BlkHeight) 453 | require.Equal(v, act.GasFee) 454 | } 455 | } 456 | 457 | func TestMainnetGetBlock(t *testing.T) { 458 | require := require.New(t) 459 | conn, err := mainnetGrpcConn() 460 | require.NoError(err) 461 | defer conn.Close() 462 | 463 | c := NewReadOnlyClient(iotexapi.NewAPIServiceClient(conn)) 464 | req := iotexapi.GetRawBlocksRequest{ 465 | Count: 1, 466 | WithReceipts: true, 467 | } 468 | type blk struct { 469 | height uint64 470 | prev, txRoot, delta, rxRoot, bloom string 471 | actSize, rxSize int 472 | } 473 | for _, v := range []blk{ 474 | {8007, "857a6e2adb32020fc700c5bb89d4eebbb173c3bd763dfdb2be3a6cf4067a05bc", "ead1f54f396bf6016bf27f45e5252cf26c1a2e5c2a69b8f7e066754d45b5f1ba", "1f8b25e0333e9794e6a61d783b66822f4efeff010a15be2f677ed02e74deea28", "fee660dcce2cf0f4f6fc09b2730c5df1a0d5ebe68d6c410b58748bedc17220c5", "", 2, 2}, 475 | {13713788, "e3c2eb8aaf531b877fa461aeb000209bbe95ac17626237952db9f25c7e89305f", "17caa398ed77b50329f90f8c9d72f423f435b29866d4295568370759cf596e69", "91ba29f363d970eb4022dade4229c7d8beadeb30e7292d56a9376fac11bb6836", "f4b59d54db803c1590428e90513a17427525f8ebfed0e5b9a10dd2cd5ad317c1", "00000000080004000081200000000100000000000000000000000000000000100000001000000200002000000000000001800080000800800000008000000000000000060000000000000800000000000082000088100800000040000000000000000a0040000000000000400000000000000000000000801000000000000000100000010000000008020000000000000000000000010000000000000000000080000000000000040000100a000800000000000000000000000000011000200000000000000000002808000010000000000000000000000800000000200000100000000050001000000000000200000000000000000001010000100010000000", 6, 6}, 476 | } { 477 | req.StartHeight = v.height 478 | res, err := c.API().GetRawBlocks(context.Background(), &req) 479 | require.NoError(err) 480 | require.Equal(1, len(res.Blocks)) 481 | blk := res.Blocks[0] 482 | require.Equal(v.rxSize, len(blk.Receipts)) 483 | core := blk.Block.Header.Core 484 | require.Equal(v.height, core.Height) 485 | require.Equal(v.prev, hex.EncodeToString(core.PrevBlockHash)) 486 | require.Equal(v.txRoot, hex.EncodeToString(core.TxRoot)) 487 | require.Equal(v.delta, hex.EncodeToString(core.DeltaStateDigest)) 488 | require.Equal(v.rxRoot, hex.EncodeToString(core.ReceiptRoot)) 489 | require.Equal(v.bloom, hex.EncodeToString(core.LogsBloom)) 490 | require.Equal(v.actSize, len(blk.Block.Body.Actions)) 491 | } 492 | } 493 | 494 | func TestMainnetGetMeta(t *testing.T) { 495 | require := require.New(t) 496 | conn, err := mainnetGrpcConn() 497 | require.NoError(err) 498 | defer conn.Close() 499 | 500 | ctx := context.Background() 501 | c := iotexapi.NewAPIServiceClient(conn) 502 | res, err := c.GetBlockMetas(ctx, &iotexapi.GetBlockMetasRequest{ 503 | Lookup: &iotexapi.GetBlockMetasRequest_ByIndex{ 504 | ByIndex: &iotexapi.GetBlockMetasByIndexRequest{ 505 | Start: 13655501, 506 | Count: 100, 507 | }, 508 | }, 509 | }) 510 | require.NoError(err) 511 | require.Equal(100, len(res.BlkMetas)) 512 | meta := res.BlkMetas[0] 513 | require.Equal(meta.Hash, "fd7b5a375300940fe0c0da482ee79a2e04440c4dc0490f2fc57e191b9c322d71") 514 | require.EqualValues(meta.Height, 13655501) 515 | require.EqualValues(meta.NumActions, 8) 516 | require.Equal(meta.ProducerAddress, "io1zy9q4myp3jezxdsv82z3f865ruamhqm7a6mggh") 517 | require.Equal(meta.TransferAmount, "0") 518 | require.Equal(meta.TxRoot, "2baa264c7bbaf4b40ab8852e9ca8c900ee7c0d50425738dd337d3831a8b332d9") 519 | require.Equal(meta.ReceiptRoot, "55d4e59f7a1f2c070300ff5eae84fcbeacd80970226dc8cb754bbbb916311878") 520 | require.Equal(meta.DeltaStateDigest, "cba67830d9a1f5d88da1bcafa7c9e79a780f0a3cf8af89b93cce50527a81c441") 521 | require.Equal(meta.LogsBloom, "00100501000000200000000024000000400004000c00008080000000000000000000000000000000000000010000000002800000000020000000000100400200000000000000410000200080804000000002000000000000000040400040004000028200000040c200000140001000000000000000002021000200000200020010000020000000000802040000000000110004000200020020005010000000040004200000000000000000002008000001000000004020000000000800302000000000000000000080200000000000000000000020020100000000000800010000480000c0000000000000400000000000020008080000820000000010000880") 522 | require.EqualValues(meta.GasLimit, 21208628) 523 | require.EqualValues(meta.GasUsed, 953375) 524 | } 525 | 526 | func TestMainnetGetReceipt(t *testing.T) { 527 | require := require.New(t) 528 | conn, err := mainnetGrpcConn() 529 | require.NoError(err) 530 | defer conn.Close() 531 | 532 | c := NewReadOnlyClient(iotexapi.NewAPIServiceClient(conn)) 533 | for _, v := range []struct { 534 | actHash, blkHash, contract string 535 | status, height, gas uint64 536 | logSize int 537 | logData string 538 | }{ 539 | {"84755e8be7ee7ba7891a57cf03b3c3dc65d2699b8935552704b900c70f38d4af", "7e42c615c7dde2297c98220520eb68b0af9d1e6ff856b793974a311aed8221b5", "", 0, 8007, 11493, 0, ""}, 540 | {"cd93427afc41e0e219b1aa6370b04ec89989e249438c7f8451a83f48503a3660", "7e42c615c7dde2297c98220520eb68b0af9d1e6ff856b793974a311aed8221b5", "io154mvzs09vkgn0hw6gg3ayzw5w39jzp47f8py9v", 1, 8007, 0, 1, "1229696f3163357a776832347063347a3837747177346d367a3663347935343470777376386e72726d36361a143136303030303030303030303030303030303030"}, 541 | } { 542 | h, err := hex.DecodeString(v.actHash) 543 | require.NoError(err) 544 | res, err := c.GetReceipt(hash.BytesToHash256(h)).Call(context.Background()) 545 | require.NoError(err) 546 | require.Equal(v.blkHash, res.ReceiptInfo.BlkHash) 547 | r := res.ReceiptInfo.Receipt 548 | require.Equal(v.status, r.Status) 549 | require.Equal(v.height, r.BlkHeight) 550 | require.Equal(v.actHash, hex.EncodeToString(r.ActHash)) 551 | require.Equal(v.gas, r.GasConsumed) 552 | require.Equal(v.contract, r.ContractAddress) 553 | require.Equal(v.logSize, len(r.Logs)) 554 | if v.logSize > 0 { 555 | // verify first log 556 | l := r.Logs[0] 557 | require.Equal(v.contract, l.ContractAddress) 558 | require.Zero(len(l.Topics)) 559 | require.Equal(v.logData, hex.EncodeToString(l.Data)) 560 | require.Equal(v.height, l.BlkHeight) 561 | require.Equal(v.actHash, hex.EncodeToString(l.ActHash)) 562 | require.Zero(l.BlkHash) // somehow defined but not used 563 | } 564 | } 565 | } 566 | 567 | func TestMainnetTransactionLogs(t *testing.T) { 568 | require := require.New(t) 569 | conn, err := mainnetGrpcConn() 570 | require.NoError(err) 571 | defer conn.Close() 572 | 573 | c := NewReadOnlyClient(iotexapi.NewAPIServiceClient(conn)) 574 | req := iotexapi.GetTransactionLogByBlockHeightRequest{ 575 | BlockHeight: 11399317, 576 | } 577 | res, err := c.API().GetTransactionLogByBlockHeight(context.Background(), &req) 578 | require.NoError(err) 579 | require.Equal(1, len(res.TransactionLogs.Logs)) 580 | log := res.TransactionLogs.Logs[0] 581 | require.Equal("fa45221706f521e3c18638610c52d45166ae5d91b8c23d89a5f4a49cba5e5b1c", hex.EncodeToString(log.ActionHash)) 582 | require.EqualValues(2, log.NumTransactions) 583 | type txLog struct { 584 | send, recv, amount string 585 | txType int 586 | } 587 | for i, v := range []txLog{ 588 | {"io1yrnwxklgkyu464zlpgx9laa6ax4203znvkc3z8", "io0000000000000000000000rewardingprotocol", "29999000000000000", 6}, 589 | {"io1yrnwxklgkyu464zlpgx9laa6ax4203znvkc3z8", "io1qp00xg6a0x864a7k68l3wm7l9zggk5wv3wwtem", "0", 0}, 590 | } { 591 | tx := log.Transactions[i] 592 | require.Equal(v.send, tx.Sender) 593 | require.Equal(v.recv, tx.Recipient) 594 | require.Equal(v.amount, tx.Amount) 595 | require.EqualValues(v.txType, tx.Type) 596 | } 597 | 598 | req.BlockHeight = 15146543 599 | res, err = c.API().GetTransactionLogByBlockHeight(context.Background(), &req) 600 | require.NoError(err) 601 | require.Equal(4, len(res.TransactionLogs.Logs)) 602 | log = res.TransactionLogs.Logs[0] 603 | require.Equal("d814942989e07a87c9a6258b369d891b6abf0dc0b10a879c6965d4d168a7c43b", hex.EncodeToString(log.ActionHash)) 604 | require.EqualValues(1, log.NumTransactions) 605 | tx := log.Transactions[0] 606 | require.Equal("io18z6st3gkxfda3t7dhksk63cgd7dr7jnkwwy22f", tx.Sender) 607 | require.Equal("io0000000000000000000000rewardingprotocol", tx.Recipient) 608 | require.Equal("155692000000000000", tx.Amount) 609 | require.EqualValues(6, tx.Type) 610 | 611 | log = res.TransactionLogs.Logs[1] 612 | require.Equal("85e65a6062bc3c35c55562bb49bc39f80fdd55a570ff9adb717f566f7d9625c4", hex.EncodeToString(log.ActionHash)) 613 | require.EqualValues(2, log.NumTransactions) 614 | for i, v := range []txLog{ 615 | {"io1lhukp867ume3qn2g7cxn4e47pj0ugfxeqj7nm8", "io1fur9ctq05wms488l3hjn2s59lpqq34wuakqv88", "61415308145556742", 7}, 616 | {"io1lhukp867ume3qn2g7cxn4e47pj0ugfxeqj7nm8", "io0000000000000000000000rewardingprotocol", "10000000000000000", 6}, 617 | } { 618 | tx := log.Transactions[i] 619 | require.Equal(v.send, tx.Sender) 620 | require.Equal(v.recv, tx.Recipient) 621 | require.Equal(v.amount, tx.Amount) 622 | require.EqualValues(v.txType, tx.Type) 623 | } 624 | 625 | log = res.TransactionLogs.Logs[2] 626 | require.Equal("d67f964c1eef51315d7484179ea0fe3529e5fa5ec7e985c8854814ffac44ed0c", hex.EncodeToString(log.ActionHash)) 627 | require.EqualValues(2, log.NumTransactions) 628 | for i, v := range []txLog{ 629 | {"io1ayw5k53sh4pfetmgj887yzuhhge42fxzruga6l", "io18q8zqlwwtkr6wwjd2smrqlfjza29mn5c4wnr46", "14650000000000000000000", 7}, 630 | {"io1ayw5k53sh4pfetmgj887yzuhhge42fxzruga6l", "io0000000000000000000000rewardingprotocol", "10000000000000000", 6}, 631 | } { 632 | tx := log.Transactions[i] 633 | require.Equal(v.send, tx.Sender) 634 | require.Equal(v.recv, tx.Recipient) 635 | require.Equal(v.amount, tx.Amount) 636 | require.EqualValues(v.txType, tx.Type) 637 | } 638 | 639 | log = res.TransactionLogs.Logs[3] 640 | require.Equal("44b857ed0514df6474c2af7e14ec09d1414585019b536f183a2e192cea71db87", hex.EncodeToString(log.ActionHash)) 641 | require.EqualValues(1, log.NumTransactions) 642 | tx = log.Transactions[0] 643 | require.Equal("io1dyj80tp303jshhvumlfkfemclqnmnr6hng95ep", tx.Sender) 644 | require.Equal("io0000000000000000000000rewardingprotocol", tx.Recipient) 645 | require.Equal("85714000000000000", tx.Amount) 646 | require.EqualValues(6, tx.Type) 647 | } 648 | 649 | func TestMainnetReadCandAndReclaim(t *testing.T) { 650 | t.Skipf("skip mainnet test because the data may be updated") 651 | require := require.New(t) 652 | conn, err := mainnetGrpcConn() 653 | require.NoError(err) 654 | defer conn.Close() 655 | 656 | c := iotexapi.NewAPIServiceClient(conn) 657 | ctx := context.Background() 658 | owner := "io16frg0hz9ztxhqkn5e42f2aznnreyy79dmqlsn2" 659 | readStakingDataRequest := &iotexapi.ReadStakingDataRequest{ 660 | Request: &iotexapi.ReadStakingDataRequest_CandidateByAddress_{ 661 | CandidateByAddress: &iotexapi.ReadStakingDataRequest_CandidateByAddress{ 662 | OwnerAddr: owner, 663 | }, 664 | }, 665 | } 666 | requestData, err := proto.Marshal(readStakingDataRequest) 667 | require.NoError(err) 668 | method := &iotexapi.ReadStakingDataMethod{ 669 | Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_ADDRESS, 670 | } 671 | methodData, err := proto.Marshal(method) 672 | require.NoError(err) 673 | rep, err := c.ReadState(ctx, &iotexapi.ReadStateRequest{ 674 | ProtocolID: []byte("staking"), 675 | MethodName: methodData, 676 | Arguments: [][]byte{requestData}, 677 | }) 678 | require.NoError(err) 679 | cand := iotextypes.CandidateV2{} 680 | require.NoError(proto.Unmarshal(rep.Data, &cand)) 681 | require.Equal("coredev", cand.Name) 682 | require.Equal(owner, cand.OwnerAddress) 683 | require.Equal("io1hgxksz37qtqq9n5n6lkkc9qhaajdklhvkh5969", cand.OperatorAddress) 684 | require.Equal("io12mgttmfa2ffn9uqvn0yn37f4nz43d248l2ga85", cand.RewardAddress) 685 | require.EqualValues(11, cand.SelfStakeBucketIdx) 686 | 687 | res, err := c.GetActions(ctx, &iotexapi.GetActionsRequest{ 688 | Lookup: &iotexapi.GetActionsRequest_ByHash{ 689 | ByHash: &iotexapi.GetActionByHashRequest{ 690 | ActionHash: "2b2419b09aeaa6e650359dea8893083b2befa7a541adce4f73504e7c380f23ed", 691 | }, 692 | }, 693 | }) 694 | require.NoError(err) 695 | act := res.ActionInfo[0].Action.Core.GetStakeTransferOwnership() 696 | require.NotNil(act) 697 | reclaim := string(act.Payload) 698 | require.Contains(reclaim, "\"{\\\"bucket\\\":1339") 699 | require.Contains(reclaim, "\\\"nonce\\\":1") 700 | require.Contains(reclaim, "\\\"recipient\\\":\\\"io10ek5h563632nu5p2ur3ysj79ctxca9zh75eayv\\\"") 701 | require.Contains(reclaim, "caaade52f022eb0e50693b7b4997dbacdb0b5b1547b095b0870e2441e2f7c72c72699ba8024261f32f2736736d39a782863f6b13dcae632bb8681d820b9d232101") 702 | } 703 | -------------------------------------------------------------------------------- /iotex/utils.go: -------------------------------------------------------------------------------- 1 | package iotex 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/golang/protobuf/proto" 7 | "github.com/iotexproject/go-pkgs/hash" 8 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 9 | 10 | "github.com/iotexproject/iotex-antenna-go/v2/account" 11 | ) 12 | 13 | func sign(a account.Account, act *iotextypes.ActionCore) (*iotextypes.Action, error) { 14 | msg, err := proto.Marshal(act) 15 | if err != nil { 16 | return nil, err 17 | } 18 | sig, err := a.Sign(msg) 19 | if err != nil { 20 | return nil, err 21 | } 22 | return &iotextypes.Action{ 23 | Core: act, 24 | SenderPubKey: a.PublicKey().Bytes(), 25 | Signature: sig, 26 | }, nil 27 | } 28 | 29 | // ActionHash computes the hash of an action 30 | func ActionHash(act *iotextypes.Action, chainid uint32) (hash.Hash256, error) { 31 | switch act.Encoding { 32 | case iotextypes.Encoding_IOTEX_PROTOBUF: 33 | ser, err := proto.Marshal(act) 34 | if err != nil { 35 | return hash.ZeroHash256, err 36 | } 37 | return hash.Hash256b(ser), nil 38 | case iotextypes.Encoding_ETHEREUM_RLP: 39 | tx, err := actionToRLP(act.Core) 40 | if err != nil { 41 | return hash.ZeroHash256, err 42 | } 43 | h, err := rlpSignedHash(tx, chainid, act.GetSignature()) 44 | if err != nil { 45 | return hash.ZeroHash256, err 46 | } 47 | return h, nil 48 | default: 49 | return hash.ZeroHash256, fmt.Errorf("invalid encoding type = %v", act.Encoding) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /iotex/utils_rlp.go: -------------------------------------------------------------------------------- 1 | package iotex 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | "github.com/ethereum/go-ethereum/rlp" 10 | "github.com/iotexproject/go-pkgs/hash" 11 | "github.com/iotexproject/iotex-address/address" 12 | "github.com/iotexproject/iotex-proto/golang/iotextypes" 13 | "golang.org/x/crypto/sha3" 14 | ) 15 | 16 | func actionToRLP(core *iotextypes.ActionCore) (*types.Transaction, error) { 17 | var ( 18 | to string 19 | amount = new(big.Int) 20 | payload []byte 21 | ) 22 | 23 | switch { 24 | case core.GetTransfer() != nil: 25 | tsf := core.GetTransfer() 26 | to = tsf.Recipient 27 | amount.SetString(tsf.Amount, 10) 28 | payload = tsf.Payload 29 | case core.GetExecution() != nil: 30 | exec := core.GetExecution() 31 | to = exec.Contract 32 | amount.SetString(exec.Amount, 10) 33 | payload = exec.Data 34 | default: 35 | return nil, fmt.Errorf("invalid action type %T not supported", core) 36 | } 37 | 38 | // generate raw tx 39 | gasPrice := new(big.Int) 40 | gasPrice.SetString(core.GetGasPrice(), 10) 41 | if to != "" { 42 | addr, err := address.FromString(to) 43 | if err != nil { 44 | return nil, err 45 | } 46 | ethAddr := common.BytesToAddress(addr.Bytes()) 47 | return types.NewTransaction(core.GetNonce(), ethAddr, amount, core.GetGasLimit(), gasPrice, payload), nil 48 | } 49 | return types.NewContractCreation(core.GetNonce(), amount, core.GetGasLimit(), gasPrice, payload), nil 50 | } 51 | 52 | func rlpSignedHash(tx *types.Transaction, chainID uint32, sig []byte) (hash.Hash256, error) { 53 | if len(sig) != 65 { 54 | return hash.ZeroHash256, fmt.Errorf("invalid signature length = %d, expecting 65", len(sig)) 55 | } 56 | sc := make([]byte, 65) 57 | copy(sc, sig) 58 | if sc[64] >= 27 { 59 | sc[64] -= 27 60 | } 61 | 62 | signedTx, err := tx.WithSignature(types.NewEIP155Signer(big.NewInt(int64(chainID))), sc) 63 | if err != nil { 64 | return hash.ZeroHash256, err 65 | } 66 | h := sha3.NewLegacyKeccak256() 67 | rlp.Encode(h, signedTx) 68 | return hash.BytesToHash256(h.Sum(nil)), nil 69 | } 70 | -------------------------------------------------------------------------------- /jwt/jwt.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package jwt 8 | 9 | import ( 10 | "encoding/hex" 11 | "errors" 12 | 13 | "github.com/golang-jwt/jwt" 14 | "github.com/iotexproject/go-pkgs/crypto" 15 | ) 16 | 17 | // const 18 | const ( 19 | CREATE = "Create" 20 | READ = "Read" 21 | UPDATE = "Update" 22 | DELETE = "Delete" 23 | ) 24 | 25 | type ( 26 | // JWT is a JWT object 27 | JWT struct { 28 | IssuedAt int64 29 | ExpiresAt int64 30 | Issuer string 31 | Subject string 32 | Scope string 33 | SignMethod string 34 | SigHex string 35 | } 36 | 37 | claimWithScope struct { 38 | jwt.StandardClaims 39 | Scope string `json:"scope,omitempty"` 40 | } 41 | ) 42 | 43 | // SignJWT creates a JWT 44 | func SignJWT(issue, expire int64, subject, scope string, key crypto.PrivateKey) (string, error) { 45 | c := &claimWithScope{ 46 | StandardClaims: jwt.StandardClaims{ 47 | ExpiresAt: expire, 48 | IssuedAt: issue, 49 | Issuer: "0x" + key.PublicKey().HexString(), 50 | Subject: subject, 51 | }, 52 | Scope: scope, 53 | } 54 | token := jwt.NewWithClaims(jwt.SigningMethodES256, c) 55 | return token.SignedString(key.EcdsaPrivateKey()) 56 | } 57 | 58 | // VerifyJWT verifies the JWT 59 | func VerifyJWT(jwtString string) (*JWT, error) { 60 | claim := &claimWithScope{} 61 | token, err := jwt.ParseWithClaims(jwtString, claim, func(token *jwt.Token) (interface{}, error) { 62 | keyHex := claim.Issuer 63 | if keyHex[:2] == "0x" || keyHex[:2] == "0X" { 64 | keyHex = keyHex[2:] 65 | } 66 | key, err := crypto.HexStringToPublicKey(keyHex) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return key.EcdsaPublicKey(), nil 71 | }) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | if !token.Valid || len(token.Header) < 2 { 77 | // should not happen with a success parsing, check anyway 78 | return nil, errors.New("invalid token") 79 | } 80 | 81 | // decode signature 82 | sig, err := jwt.DecodeSegment(token.Signature) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | return &JWT{ 88 | IssuedAt: claim.IssuedAt, 89 | ExpiresAt: claim.ExpiresAt, 90 | Issuer: claim.Issuer, 91 | Subject: claim.Subject, 92 | Scope: claim.Scope, 93 | SignMethod: token.Header["alg"].(string), 94 | SigHex: hex.EncodeToString(sig), 95 | }, nil 96 | } 97 | -------------------------------------------------------------------------------- /jwt/jwt_test.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "encoding/hex" 5 | "strings" 6 | "testing" 7 | "time" 8 | 9 | "github.com/golang-jwt/jwt" 10 | "github.com/iotexproject/go-pkgs/crypto" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestDecodeJWT(t *testing.T) { 15 | r := require.New(t) 16 | 17 | header := "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9" 18 | payload := "eyJpYXQiOjE2MDU3NzQ5NjksImlzcyI6IjB4MDQ2MGVkNzAyYmY1YWNmZmE0NjM1ZWM1OTQ0OGZkNDFkOTQyMGNjMDc4NjZiMDc0M2VjMTdiNGJiYjI3YWZhZjA4NGNkOTc0M2Y1Y2Q1MjAwOWFmYzQxOTMyNDNiYWRkOGUyZGVmZGEyNGYxM2MzMjY5YzQ4OTkwM2Q1OWRkZWJlMCIsInN1YiI6Imh0dHA6Ly9leGFtcGxlLmNvbWUvMTIzNCJ9" 19 | signature := "ftpWYERxNYYkDrFVDVYao-kofdMVu8_J3GcKxF1JQOhwtscW9d3BAsrxFnKQ5p2o6XYRKiDHxOX_gCXGMymM3A" 20 | 21 | // JWT token = header.payload.signature 22 | jwtStr := header + "." + payload + "." + signature 23 | tok, err := jwt.Parse(jwtStr, func(token *jwt.Token) (interface{}, error) { 24 | keyHex := token.Claims.(jwt.MapClaims)["iss"] 25 | key, err := crypto.HexStringToPublicKey(keyHex.(string)[2:]) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return key.EcdsaPublicKey(), nil 30 | }) 31 | r.NoError(err) 32 | 33 | // header = 34 | // { 35 | // "alg": "ES256", 36 | // "typ": "JWT" 37 | // } 38 | r.Equal("ES256", tok.Header["alg"].(string)) 39 | r.Equal("JWT", tok.Header["typ"].(string)) 40 | 41 | // payload = 42 | // { 43 | // "exp": 1605772249, 44 | // "iss": "0x0460ed702bf5acffa4635ec59448fd41d9420cc07866b0743ec17b4bbb27afaf084cd9743f5cd52009afc4193243badd8e2defda24f13c3269c489903d59ddebe0", 45 | // "sub": "http://example.come/1234" 46 | // } 47 | iss := "0x0460ed702bf5acffa4635ec59448fd41d9420cc07866b0743ec17b4bbb27afaf084cd9743f5cd52009afc4193243badd8e2defda24f13c3269c489903d59ddebe0" 48 | claim := tok.Claims.(jwt.MapClaims) 49 | r.Equal(3, len(claim)) 50 | r.EqualValues(1605774969, claim["iat"].(float64)) 51 | r.Equal(iss, claim["iss"].(string)) 52 | r.Equal("http://example.come/1234", claim["sub"].(string)) 53 | 54 | // signature 55 | r.Equal(signature, tok.Signature) 56 | 57 | // verify JWT 58 | token, err := VerifyJWT(jwtStr) 59 | r.NoError(err) 60 | r.EqualValues(1605774969, token.IssuedAt) 61 | r.EqualValues(0, token.ExpiresAt) 62 | r.Equal(iss, token.Issuer) 63 | r.Equal("http://example.come/1234", token.Subject) 64 | r.Equal("ES256", token.SignMethod) 65 | 66 | // signature = ecdsaSign(hash(header.payload)) 67 | hasher := jwt.SigningMethodES256.Hash.New() 68 | hasher.Write([]byte(header + "." + payload)) 69 | sigBytes, err := hex.DecodeString(token.SigHex) 70 | r.NoError(err) 71 | // recover public key from signature and verify 72 | pk, err := crypto.RecoverPubkey(hasher.Sum(nil), append(sigBytes, 1)) 73 | r.NoError(err) 74 | r.Equal(iss[2:], pk.HexString()) 75 | } 76 | 77 | func TestSignVerifyJWT(t *testing.T) { 78 | r := require.New(t) 79 | 80 | a, err := crypto.GenerateKey() 81 | r.NoError(err) 82 | 83 | now := time.Now().Unix() 84 | jwtTests := []struct { 85 | iss, exp int64 86 | url, scope string 87 | errStr string 88 | }{ 89 | {now, 0, "http://example.come/1234", CREATE, ""}, 90 | {now, now + 1, "http://example.come/1234", READ, ""}, 91 | {now, now + 2, "http://example.come/4321", DELETE, ""}, 92 | {now, now - 1, "http://example.come/1234", "", "token is expired by"}, 93 | {now + 1, now, "http://example.come/1234", "", "Token used before issued"}, 94 | } 95 | 96 | issuer := "0x" + a.PublicKey().HexString() 97 | for _, v := range jwtTests { 98 | jwtStr, err := SignJWT(v.iss, v.exp, v.url, v.scope, a) 99 | r.NoError(err) 100 | token, err := VerifyJWT(jwtStr) 101 | if v.errStr != "" { 102 | r.True(strings.HasPrefix(err.Error(), v.errStr)) 103 | continue 104 | } 105 | r.NoError(err) 106 | r.Equal(v.iss, token.IssuedAt) 107 | r.Equal(v.exp, token.ExpiresAt) 108 | r.Equal(issuer, token.Issuer) 109 | r.Equal(v.url, token.Subject) 110 | r.Equal(v.scope, token.Scope) 111 | r.Equal("ES256", token.SignMethod) 112 | 113 | // signing the token with a diff key fails the verification 114 | claim := &claimWithScope{ 115 | StandardClaims: jwt.StandardClaims{ 116 | ExpiresAt: v.exp, 117 | IssuedAt: v.iss, 118 | Issuer: issuer, 119 | Subject: v.url, 120 | }, 121 | Scope: v.scope, 122 | } 123 | tok := jwt.NewWithClaims(jwt.SigningMethodES256, claim) 124 | str, err := tok.SigningString() 125 | r.NoError(err) 126 | b, err := crypto.GenerateKey() 127 | r.NoError(err) 128 | sig, err := tok.Method.Sign(str, b.EcdsaPrivateKey()) 129 | r.NoError(err) 130 | str = strings.Join([]string{str, sig}, ".") 131 | r.NotEqual(jwtStr, str) 132 | _, err = VerifyJWT(str) 133 | r.Equal(jwt.ErrECDSAVerification.Error(), err.Error()) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /utils/unit/unit.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package unit 8 | 9 | import "math/big" 10 | 11 | // IotexUnit defines iotex unit type. 12 | type IotexUnit int64 13 | 14 | const ( 15 | // Rau is the smallest non-fungible token unit 16 | Rau IotexUnit = 1 17 | // KRau is 1000 Rau 18 | KRau = Rau * 1000 19 | // MRau is 1000 KRau 20 | MRau = KRau * 1000 21 | // GRau is 1000 MRau 22 | GRau = MRau * 1000 23 | // Qev is 1000 GRau 24 | Qev = GRau * 1000 25 | // Jin is 1000 Qev 26 | Jin = Qev * 1000 27 | // Iotx is 1000 Jin, which should be fit into int64 28 | Iotx = Jin * 1000 29 | ) 30 | 31 | // FromString converts string to IotexUnit. 32 | func (u *IotexUnit) FromString(s string) { 33 | var unitInt IotexUnit 34 | switch s { 35 | case "Rau": 36 | unitInt = Rau 37 | case "KRau": 38 | unitInt = KRau 39 | case "MRau": 40 | unitInt = MRau 41 | case "GRau": 42 | unitInt = GRau 43 | case "Qev": 44 | unitInt = Qev 45 | case "Jin": 46 | unitInt = Jin 47 | default: 48 | unitInt = Iotx 49 | } 50 | if u == nil { 51 | u = &unitInt 52 | } else { 53 | *u = unitInt 54 | } 55 | } 56 | 57 | // FromRau converts Rau string into diffrent unit string 58 | func FromRau(rau *big.Int, unit string) *big.Int { 59 | n := big.NewInt(0).Set(rau) 60 | u := IotexUnit(0) 61 | u.FromString(unit) 62 | return n.Div(n, big.NewInt(int64(u))) 63 | } 64 | 65 | // ToRau converts different unit string into Rau string 66 | func ToRau(num *big.Int, unit string) *big.Int { 67 | n := big.NewInt(0).Set(num) 68 | u := IotexUnit(0) 69 | u.FromString(unit) 70 | return n.Mul(n, big.NewInt(int64(u))) 71 | } 72 | -------------------------------------------------------------------------------- /utils/unit/unit_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package unit 8 | 9 | import ( 10 | "math/big" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestFromRau(t *testing.T) { 17 | require := require.New(t) 18 | n, _ := big.NewInt(0).SetString("1000", 10) 19 | convert := FromRau(n, "KRau") 20 | require.Equal("1", convert.Text(10)) 21 | 22 | n, _ = big.NewInt(0).SetString("1000000000000000000", 10) 23 | convert = FromRau(n, "Iotx") 24 | require.Equal("1", convert.Text(10)) 25 | } 26 | func TestToRau(t *testing.T) { 27 | require := require.New(t) 28 | n, _ := big.NewInt(0).SetString("1", 10) 29 | convert := ToRau(n, "Iotx") 30 | require.Equal("1000000000000000000", convert.Text(10)) 31 | 32 | convert = ToRau(n, "GRau") 33 | require.Equal("1000000000", convert.Text(10)) 34 | } 35 | -------------------------------------------------------------------------------- /utils/wait/wait.go: -------------------------------------------------------------------------------- 1 | package wait 2 | 3 | import ( 4 | "context" 5 | "encoding/hex" 6 | "time" 7 | 8 | "github.com/cenkalti/backoff" 9 | "github.com/iotexproject/iotex-antenna-go/v2/iotex" 10 | "github.com/iotexproject/iotex-proto/golang/iotexapi" 11 | "github.com/pkg/errors" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | // Wait waits on a send action caller to finish (wait 20 second), then fetch the receipt of the action, 16 | // to make sure the action is on chain. 17 | func Wait(ctx context.Context, caller iotex.Caller, opts ...grpc.CallOption) error { 18 | h, err := caller.Call(ctx, opts...) 19 | if err != nil { 20 | return err 21 | } 22 | 23 | time.Sleep(25 * time.Second) 24 | 25 | var response *iotexapi.GetReceiptByActionResponse 26 | rerr := backoff.Retry(func() error { 27 | response, err = caller.API().GetReceiptByAction(ctx, &iotexapi.GetReceiptByActionRequest{ 28 | ActionHash: hex.EncodeToString(h[:]), 29 | }, opts...) 30 | return err 31 | }, backoff.WithMaxRetries(backoff.NewConstantBackOff(30*time.Second), 3)) 32 | if rerr != nil { 33 | return errors.Errorf("execution get receipt timed out: %v", rerr) 34 | } 35 | if response.ReceiptInfo.Receipt.Status != 1 { 36 | return errors.Errorf("execution failed: %x", h) 37 | } 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 IoTeX 2 | // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no 3 | // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent 4 | // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache 5 | // License 2.0 that can be found in the LICENSE file. 6 | 7 | package version 8 | 9 | const ( 10 | // ProtocolVersion defines Protocol version, starting from 1 11 | ProtocolVersion = 0x01 12 | ) 13 | 14 | var ( 15 | // PackageVersion gets version of code from git tag 16 | PackageVersion = "NoBuildInfo" 17 | // PackageCommitID gets latest commit id of code from git 18 | PackageCommitID = "NoBuildInfo" 19 | // GitStatus gets git tree status from git 20 | GitStatus = "NoBuildInfo" 21 | // GoVersion gets go version of package 22 | GoVersion = "NoBuildInfo" 23 | // BuildTime gets building time 24 | BuildTime = "NoBuildInfo" 25 | ) 26 | --------------------------------------------------------------------------------