├── glide.yaml ├── .travis.yml ├── .gitignore ├── test-with-coverage.sh ├── glide.lock ├── README.md ├── LICENSE ├── provider ├── provider.go ├── http_provider.go └── http_provider_test.go ├── rpc ├── rpc.go ├── jsonrpc_test.go └── jsonrpc.go ├── web3 ├── request_manager.go ├── net_test.go ├── net.go ├── filter.go ├── web3_test.go ├── types.go ├── web3.go ├── eth_test.go └── eth.go ├── test ├── mock.go ├── mock_net.go ├── mock_http_provider.go └── mock_eth.go ├── example ├── client │ └── main.go └── filter │ └── main.go └── common ├── util.go └── types.go /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/alanchchen/web3go 2 | import: 3 | - package: github.com/stretchr/testify 4 | subpackages: 5 | - mock 6 | - package: github.com/tonnerre/golang-go.crypto 7 | subpackages: 8 | - sha3 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.6.1 4 | install: 5 | - go get golang.org/x/tools/cmd/cover 6 | - go get github.com/Masterminds/glide 7 | before_script: 8 | - glide install 9 | script: 10 | - ./test-with-coverage.sh 11 | after_success: 12 | - bash <(curl -s https://codecov.io/bash) 13 | sudo: false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | vendor 27 | .vscode 28 | .DS_Store 29 | coverage.txt 30 | -------------------------------------------------------------------------------- /test-with-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(find . -type d -name 'vendor*' -prune -o -type d -name 'test' -prune -o -type d -print); do 7 | if ls $d/*.go &> /dev/null; then 8 | go test -v -coverprofile=profile.out -covermode=atomic $d 9 | if [ -f profile.out ]; then 10 | cat profile.out >> coverage.txt 11 | rm profile.out 12 | fi 13 | fi 14 | done 15 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 4abc716f7e5f617c7ea16c75586d5dc4f5989d4f4281acac4667462ec3d50e5b 2 | updated: 2016-06-12T17:53:36.012128208+08:00 3 | imports: 4 | - name: github.com/davecgh/go-spew 5 | version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d 6 | subpackages: 7 | - spew 8 | - name: github.com/pmezard/go-difflib 9 | version: d8ed2627bdf02c080bf22230dbb337003b7aba2d 10 | subpackages: 11 | - difflib 12 | - name: github.com/stretchr/objx 13 | version: cbeaeb16a013161a98496fad62933b1d21786672 14 | - name: github.com/stretchr/testify 15 | version: 8d64eb7173c7753d6419fd4a9caf057398611364 16 | subpackages: 17 | - mock 18 | - assert 19 | - name: github.com/tonnerre/golang-go.crypto 20 | version: 9bbb332f040b171b2fb4e08c53a3db3b3f6633c0 21 | subpackages: 22 | - sha3 23 | devImports: [] 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web3go (obsolete) 2 | `web3go` is an [Ethereum](https://github.com/ethereum/go-ethereum) client library that implements [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC), just like [web3.js](https://githu 3 | b.com/ethereum/web3.js). 4 | 5 | [![Gitter](https://badges.gitter.im/alanchchen/web3go.svg)](https://gitter.im/alanchchen/web3go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Go Report Card](https://goreportcard.com/badge/github.com/alanchchen/web3go)](https://goreportcard.com/report/github.com/alanchchen/web3go) 6 | 7 | | Branch | Test status | 8 | |:-------:|:-----------:| 9 | | master | [![Test Status](https://travis-ci.org/alanchchen/web3go.svg?branch=master)](https://travis-ci.org/alanchchen/web3go) [![codecov](https://codecov.io/gh/alanchchen/web3go/branch/master/graph/badge.svg)](https://codecov.io/gh/alanchchen/web3go/branch/master) | 10 | | develop | [![Test Status](https://travis-ci.org/alanchchen/web3go.svg?branch=develop)](https://travis-ci.org/alanchchen/web3go/) [![codecov](https://codecov.io/gh/alanchchen/web3go/branch/develop/graph/badge.svg)](https://codecov.io/gh/alanchchen/web3go/branch/develop) | 11 | 12 | # Latest version 13 | No stable version yet 14 | 15 | # Installation 16 | ```shell 17 | go get github.com/alanchchen/web3go/... 18 | ``` 19 | 20 | # Usage 21 | TBD 22 | 23 | # License 24 | The BSD 3-Clause License 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Alan Chen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of web3go nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /provider/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package provider 31 | 32 | import ( 33 | "github.com/alanchchen/web3go/rpc" 34 | ) 35 | 36 | // Provider provides basic web3 interface 37 | type Provider interface { 38 | IsConnected() bool 39 | Send(rpc.Request) (rpc.Response, error) 40 | GetRPCMethod() rpc.RPC 41 | } 42 | -------------------------------------------------------------------------------- /rpc/rpc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package rpc 31 | 32 | // Request defines basic methods of an RPC request 33 | type Request interface { 34 | Set(key string, value interface{}) 35 | Get(key string) interface{} 36 | String() string 37 | ID() uint64 38 | } 39 | 40 | // Response defines basic methods of an RPC response 41 | type Response interface { 42 | Get(key string) interface{} 43 | String() string 44 | ID() uint64 45 | Error() error 46 | } 47 | 48 | // RPC defines basic methods of variety RPCs 49 | type RPC interface { 50 | Name() string 51 | NewRequest(method string, args ...interface{}) Request 52 | NewResponse(data []byte) Response 53 | } 54 | 55 | // GetDefaultMethod ... 56 | func GetDefaultMethod() RPC { 57 | return NewJSONRPC() 58 | } 59 | -------------------------------------------------------------------------------- /web3/request_manager.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "github.com/alanchchen/web3go/provider" 34 | "github.com/alanchchen/web3go/rpc" 35 | ) 36 | 37 | // requestManager is responsible for passing messages to providers 38 | type requestManager struct { 39 | provider provider.Provider 40 | rpc rpc.RPC 41 | } 42 | 43 | func newRequestManager(provider provider.Provider) *requestManager { 44 | return &requestManager{provider: provider, rpc: provider.GetRPCMethod()} 45 | } 46 | 47 | func (rm *requestManager) newRequest(method string) rpc.Request { 48 | return rm.rpc.NewRequest(method) 49 | } 50 | 51 | func (rm *requestManager) send(request rpc.Request) (rpc.Response, error) { 52 | return rm.provider.Send(request) 53 | } 54 | -------------------------------------------------------------------------------- /test/mock.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package test 31 | 32 | import ( 33 | "encoding/json" 34 | "fmt" 35 | 36 | "github.com/alanchchen/web3go/rpc" 37 | ) 38 | 39 | func generateResponse(rpc rpc.RPC, request rpc.Request, result interface{}) (response rpc.Response, err error) { 40 | data := struct { 41 | Version string `json:"jsonrpc"` 42 | ID uint64 `json:"id"` 43 | Result interface{} `json:"result"` 44 | }{ 45 | request.Get("version").(string), 46 | request.ID(), 47 | result, 48 | } 49 | rawData, err := json.Marshal(data) 50 | if err != nil { 51 | return nil, err 52 | } 53 | if resp := rpc.NewResponse(rawData); resp != nil { 54 | return resp, nil 55 | } 56 | return nil, fmt.Errorf("Failed to generate response") 57 | } 58 | -------------------------------------------------------------------------------- /test/mock_net.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package test 31 | 32 | import ( 33 | "fmt" 34 | 35 | "github.com/alanchchen/web3go/rpc" 36 | ) 37 | 38 | // MockNetAPI ... 39 | type MockNetAPI struct { 40 | rpc rpc.RPC 41 | } 42 | 43 | // NewMockNetAPI ... 44 | func NewMockNetAPI(rpc rpc.RPC) MockAPI { 45 | return &MockNetAPI{rpc: rpc} 46 | } 47 | 48 | // Do ... 49 | func (net *MockNetAPI) Do(request rpc.Request) (response rpc.Response, err error) { 50 | method := request.Get("method").(string) 51 | switch method { 52 | case "net_version": 53 | return generateResponse(net.rpc, request, "100") 54 | case "net_listening": 55 | return generateResponse(net.rpc, request, true) 56 | case "net_peerCount": 57 | return generateResponse(net.rpc, request, "0x32") 58 | } 59 | 60 | return nil, fmt.Errorf("Invalid method %s", method) 61 | } 62 | -------------------------------------------------------------------------------- /example/client/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package main 31 | 32 | import ( 33 | "flag" 34 | "fmt" 35 | 36 | "github.com/alanchchen/web3go/provider" 37 | "github.com/alanchchen/web3go/rpc" 38 | "github.com/alanchchen/web3go/web3" 39 | ) 40 | 41 | var hostname = flag.String("hostname", "localhost", "The ethereum client RPC host") 42 | var port = flag.String("port", "8545", "The ethereum client RPC port") 43 | var verbose = flag.Bool("verbose", false, "Print verbose messages") 44 | 45 | func main() { 46 | flag.Parse() 47 | 48 | if *verbose { 49 | fmt.Printf("Connect to %s:%s\n", *hostname, *port) 50 | } 51 | 52 | provider := provider.NewHTTPProvider(*hostname+":"+*port, rpc.GetDefaultMethod()) 53 | web3 := web3.NewWeb3(provider) 54 | 55 | if accounts, err := web3.Eth.Accounts(); err == nil { 56 | for _, account := range accounts { 57 | fmt.Printf("%s\n", account.String()) 58 | } 59 | } else { 60 | fmt.Printf("%v", err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /web3/net_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "testing" 34 | 35 | "github.com/alanchchen/web3go/test" 36 | "github.com/stretchr/testify/assert" 37 | "github.com/stretchr/testify/suite" 38 | ) 39 | 40 | type NetTestSuite struct { 41 | suite.Suite 42 | web3 *Web3 43 | net Net 44 | } 45 | 46 | func (suite *NetTestSuite) Test_Version() { 47 | net := suite.net 48 | version, err := net.Version() 49 | assert.NoError(suite.T(), err, "Should be no error") 50 | assert.NotEqual(suite.T(), "", version, "version is empty") 51 | } 52 | 53 | func (suite *NetTestSuite) Test_Listening() { 54 | net := suite.net 55 | listening, err := net.Listening() 56 | assert.NoError(suite.T(), err, "Should be no error") 57 | assert.Exactly(suite.T(), true, listening, "should be true") 58 | } 59 | 60 | func (suite *NetTestSuite) Test_PeerCount() { 61 | net := suite.net 62 | count, err := net.PeerCount() 63 | assert.NoError(suite.T(), err, "Should be no error") 64 | assert.EqualValues(suite.T(), 50, count, "should be equal") 65 | } 66 | 67 | func (suite *NetTestSuite) SetupTest() { 68 | suite.web3 = NewWeb3(test.NewMockHTTPProvider()) 69 | suite.net = suite.web3.Net 70 | } 71 | 72 | func Test_NetTestSuite(t *testing.T) { 73 | suite.Run(t, new(NetTestSuite)) 74 | } 75 | -------------------------------------------------------------------------------- /example/filter/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package main 31 | 32 | import ( 33 | "flag" 34 | "fmt" 35 | 36 | "github.com/alanchchen/web3go/provider" 37 | "github.com/alanchchen/web3go/rpc" 38 | "github.com/alanchchen/web3go/web3" 39 | ) 40 | 41 | var hostname = flag.String("hostname", "localhost", "The ethereum client RPC host") 42 | var port = flag.String("port", "8545", "The ethereum client RPC port") 43 | var verbose = flag.Bool("verbose", false, "Print verbose messages") 44 | 45 | func main() { 46 | flag.Parse() 47 | 48 | if *verbose { 49 | fmt.Printf("Connect to %s:%s\n", *hostname, *port) 50 | } 51 | 52 | provider := provider.NewHTTPProvider(*hostname+":"+*port, rpc.GetDefaultMethod()) 53 | web3Api := web3.NewWeb3(provider) 54 | 55 | filter, err := web3Api.Eth.NewBlockFilter() 56 | if err != nil { 57 | fmt.Printf("Failed to create filter, %v\n", err) 58 | return 59 | } 60 | defer func() { 61 | if _, err := web3Api.Eth.UninstallFilter(filter); err != nil { 62 | fmt.Printf("UninstallFilter failed: %v\n", err) 63 | } 64 | 65 | }() 66 | 67 | fmt.Printf("Filter ID: 0x%x\n", filter.ID()) 68 | 69 | if filterCh := filter.Watch(); filterCh != nil { 70 | for { 71 | log, err := filterCh.Next() 72 | if err == nil { 73 | fmt.Printf("Block: %v\n", log) 74 | } else { 75 | fmt.Printf("%v\n", err) 76 | return 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /common/util.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package common 31 | 32 | import ( 33 | "bytes" 34 | "encoding/gob" 35 | "regexp" 36 | "strconv" 37 | "strings" 38 | ) 39 | 40 | func IsHex(hex string) bool { 41 | hexMatcher := regexp.MustCompile("0[xX][0-9a-fA-F]+") 42 | return hexMatcher.MatchString(hex) 43 | } 44 | 45 | func HexToString(hex string) string { 46 | if strings.Index(hex, "0x") == 0 || strings.Index(hex, "0X") == 0 { 47 | return hex[2:] 48 | } 49 | return hex 50 | } 51 | 52 | func BytesToHex(data []byte) string { 53 | buffer := new(bytes.Buffer) 54 | for _, b := range data { 55 | s := strconv.FormatInt(int64(b&0xFF), 16) 56 | if len(s) == 1 { 57 | buffer.WriteString("0") 58 | } 59 | buffer.WriteString(s) 60 | } 61 | return "0x" + buffer.String() 62 | } 63 | 64 | func HexToBytes(hex string) []byte { 65 | hex = HexToString(hex) 66 | length := len(hex) / 2 67 | slice := make([]byte, length) 68 | rs := []rune(hex) 69 | 70 | for i := 0; i < length; i++ { 71 | s := string(rs[i*2 : i*2+2]) 72 | value, _ := strconv.ParseInt(s, 16, 10) 73 | slice[i] = byte(value & 0xFF) 74 | } 75 | return slice 76 | } 77 | 78 | func StringToAddress(s string) (addr Address) { 79 | b := HexToBytes(s) 80 | copy(addr[:], b) 81 | return addr 82 | } 83 | 84 | func StringToHash(s string) (hash Hash) { 85 | b := HexToBytes(s) 86 | copy(hash[:], b) 87 | return hash 88 | } 89 | 90 | func ToBytes(data interface{}) ([]byte, error) { 91 | var buf bytes.Buffer 92 | enc := gob.NewEncoder(&buf) 93 | err := enc.Encode(data) 94 | if err != nil { 95 | return nil, err 96 | } 97 | return buf.Bytes(), nil 98 | } 99 | -------------------------------------------------------------------------------- /web3/net.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "strconv" 34 | 35 | "github.com/alanchchen/web3go/common" 36 | ) 37 | 38 | // Net ... 39 | type Net interface { 40 | Version() (string, error) 41 | PeerCount() (uint64, error) 42 | Listening() (bool, error) 43 | } 44 | 45 | // NetAPI ... 46 | type NetAPI struct { 47 | requestManager *requestManager 48 | } 49 | 50 | // NewNetAPI ... 51 | func newNetAPI(requestManager *requestManager) Net { 52 | return &NetAPI{requestManager: requestManager} 53 | } 54 | 55 | // Version returns the current network protocol version. 56 | func (net *NetAPI) Version() (string, error) { 57 | req := net.requestManager.newRequest("net_version") 58 | resp, err := net.requestManager.send(req) 59 | if err != nil { 60 | return "", err 61 | } 62 | return resp.Get("result").(string), nil 63 | } 64 | 65 | // PeerCount returns number of peers currenly connected to the client. 66 | func (net *NetAPI) PeerCount() (uint64, error) { 67 | req := net.requestManager.newRequest("net_peerCount") 68 | resp, err := net.requestManager.send(req) 69 | if err != nil { 70 | return 0, err 71 | } 72 | result, err := strconv.ParseUint(common.HexToString(resp.Get("result").(string)), 16, 64) 73 | if err != nil { 74 | return 0, err 75 | } 76 | return result, nil 77 | } 78 | 79 | // Listening returns true if client is actively listening for network connections. 80 | func (net *NetAPI) Listening() (bool, error) { 81 | req := net.requestManager.newRequest("net_listening") 82 | resp, err := net.requestManager.send(req) 83 | if err != nil { 84 | return false, err 85 | } 86 | return resp.Get("result").(bool), nil 87 | } 88 | -------------------------------------------------------------------------------- /test/mock_http_provider.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package test 31 | 32 | import ( 33 | "fmt" 34 | "strings" 35 | 36 | "github.com/alanchchen/web3go/provider" 37 | "github.com/alanchchen/web3go/rpc" 38 | "github.com/stretchr/testify/mock" 39 | ) 40 | 41 | // MockAPI ... 42 | type MockAPI interface { 43 | Do(rpc.Request) (rpc.Response, error) 44 | } 45 | 46 | // MockHTTPProvider provides basic web3 interface 47 | type MockHTTPProvider struct { 48 | mock mock.Mock 49 | rpc rpc.RPC 50 | apis map[string]MockAPI 51 | } 52 | 53 | // NewMockHTTPProvider creates a HTTP provider mock 54 | func NewMockHTTPProvider() provider.Provider { 55 | method := rpc.GetDefaultMethod() 56 | return &MockHTTPProvider{rpc: method, 57 | apis: map[string]MockAPI{ 58 | "net": NewMockNetAPI(method), 59 | "eth": NewMockEthAPI(method), 60 | }} 61 | } 62 | 63 | // IsConnected ... 64 | func (provider *MockHTTPProvider) IsConnected() bool { 65 | return true 66 | } 67 | 68 | // Send JSON RPC request through http client 69 | func (provider *MockHTTPProvider) Send(request rpc.Request) (response rpc.Response, err error) { 70 | m := request.Get("method") 71 | switch m.(type) { 72 | case string: 73 | method := m.(string) 74 | return provider.dispatchMethod(method, request) 75 | default: 76 | return nil, fmt.Errorf("Invalid method %v", m) 77 | } 78 | } 79 | 80 | func (provider *MockHTTPProvider) dispatchMethod(method string, request rpc.Request) (response rpc.Response, err error) { 81 | if index := strings.Index(method, "_"); index > 0 { 82 | if api, ok := provider.apis[method[:index]]; ok { 83 | return api.Do(request) 84 | } 85 | } 86 | return nil, fmt.Errorf("Unrecognized method %s", method) 87 | } 88 | 89 | func (provider *MockHTTPProvider) GetRPCMethod() rpc.RPC { 90 | return provider.rpc 91 | } 92 | -------------------------------------------------------------------------------- /provider/http_provider.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package provider 31 | 32 | import ( 33 | "fmt" 34 | "io/ioutil" 35 | "net/http" 36 | "strings" 37 | 38 | "github.com/alanchchen/web3go/rpc" 39 | ) 40 | 41 | // HTTPProvider provides basic web3 interface 42 | type HTTPProvider struct { 43 | host string 44 | rpc rpc.RPC 45 | } 46 | 47 | // NewHTTPProvider creates a HTTP provider 48 | func NewHTTPProvider(host string, method rpc.RPC) Provider { 49 | if !strings.HasPrefix(host, "http://") { 50 | host = "http://" + host 51 | } 52 | if method == nil { 53 | method = rpc.GetDefaultMethod() 54 | } 55 | return &HTTPProvider{host: host, rpc: method} 56 | } 57 | 58 | // IsConnected ... 59 | func (provider *HTTPProvider) IsConnected() bool { 60 | req := provider.rpc.NewRequest("net_listening") 61 | resp, err := provider.Send(req) 62 | if err != nil { 63 | return false 64 | } 65 | return resp.Get("result").(bool) 66 | } 67 | 68 | // Send JSON RPC request through http client 69 | func (provider *HTTPProvider) Send(request rpc.Request) (response rpc.Response, err error) { 70 | contentType := provider.determineContentType() 71 | resp, err := http.Post(provider.host, contentType, strings.NewReader(request.String())) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | defer resp.Body.Close() 77 | body, err := ioutil.ReadAll(resp.Body) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | response = provider.rpc.NewResponse(body) 83 | if response == nil { 84 | err = fmt.Errorf("Malformed response body, %s", string(body)) 85 | } 86 | return response, err 87 | } 88 | 89 | func (provider *HTTPProvider) GetRPCMethod() rpc.RPC { 90 | return provider.rpc 91 | } 92 | 93 | func (provider *HTTPProvider) determineContentType() string { 94 | switch provider.rpc.Name() { 95 | case "jsonrpc": 96 | return "application/json" 97 | default: 98 | return "application/json" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /provider/http_provider_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package provider 31 | 32 | import ( 33 | "encoding/json" 34 | "net/http" 35 | "net/http/httptest" 36 | "testing" 37 | 38 | "github.com/alanchchen/web3go/rpc" 39 | "github.com/stretchr/testify/assert" 40 | "github.com/stretchr/testify/suite" 41 | ) 42 | 43 | type HTTPProviderTestSuite struct { 44 | suite.Suite 45 | server *httptest.Server 46 | provider Provider 47 | } 48 | 49 | func (suite *HTTPProviderTestSuite) Test_IsConnected() { 50 | provider := suite.provider 51 | assert.EqualValues(suite.T(), true, provider.IsConnected(), "should be equal") 52 | } 53 | 54 | func (suite *HTTPProviderTestSuite) Test_Send() { 55 | provider := suite.provider 56 | req := &rpc.JSONRPCRequest{ 57 | Version: "2.0", 58 | Method: "test_method", 59 | Params: nil, 60 | Identifier: 10} 61 | resp, err := provider.Send(req) 62 | 63 | assert.NoError(suite.T(), err, "Should be no error") 64 | assert.EqualValues(suite.T(), req.Version, req.Version, "should be equal") 65 | assert.EqualValues(suite.T(), req.Identifier, resp.ID(), "should be equal") 66 | assert.EqualValues(suite.T(), "ok", resp.Get("result").(string), "should be equal") 67 | } 68 | 69 | func (suite *HTTPProviderTestSuite) Test_GetRPCMethod() { 70 | provider := suite.provider 71 | assert.NotNil(suite.T(), provider.GetRPCMethod(), "should be equal") 72 | } 73 | 74 | func (suite *HTTPProviderTestSuite) SetupTest() { 75 | suite.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 76 | decoder := json.NewDecoder(r.Body) 77 | req := rpc.JSONRPCRequest{} 78 | resp := rpc.JSONRPCResponse{Version: "2.0"} 79 | err := decoder.Decode(&req) 80 | if err != nil { 81 | resp.Identifier = 0 82 | resp.Result = "error" 83 | } else { 84 | resp.Identifier = req.Identifier 85 | switch req.Method { 86 | case "net_listening": 87 | resp.Result = true 88 | default: 89 | resp.Result = "ok" 90 | } 91 | } 92 | jsonBlob, _ := json.Marshal(resp) 93 | w.Write(jsonBlob) 94 | })) 95 | suite.provider = NewHTTPProvider(suite.server.URL, rpc.GetDefaultMethod()) 96 | } 97 | 98 | func (suite *HTTPProviderTestSuite) TearDownTest() { 99 | suite.server.Close() 100 | } 101 | 102 | func Test_HTTPProviderTestSuite(t *testing.T) { 103 | suite.Run(t, new(HTTPProviderTestSuite)) 104 | } 105 | -------------------------------------------------------------------------------- /web3/filter.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "encoding/json" 34 | "errors" 35 | "sync" 36 | "time" 37 | 38 | "github.com/alanchchen/web3go/common" 39 | ) 40 | 41 | var ( 42 | ErrChannelClosed = errors.New("Channel is closed") 43 | ) 44 | 45 | const ( 46 | pollInterval = 100 * time.Millisecond 47 | dataBufferSize = 16 48 | ) 49 | 50 | type FilterType int 51 | 52 | const ( 53 | TypeNormal FilterType = iota 54 | TypeBlockFilter 55 | TypeTransactionFilter 56 | ) 57 | 58 | // FilterOption ... 59 | type FilterOption struct { 60 | FromBlock string `json:"fromBlock,omitempty"` 61 | ToBlock string `json:"toBlock,omitempty"` 62 | Address interface{} `json:"address,omitempty"` 63 | Topics common.Topics `json:"topics,omitempty"` 64 | } 65 | 66 | func (opt *FilterOption) String() string { 67 | rawBytes, _ := json.Marshal(opt) 68 | return string(rawBytes) 69 | } 70 | 71 | // Filter ... 72 | type Filter interface { 73 | Watch() WatchChannel 74 | ID() uint64 75 | } 76 | 77 | type baseFilter struct { 78 | eth Eth 79 | filterType FilterType 80 | filterID uint64 81 | } 82 | 83 | // WatchChannel ... 84 | type WatchChannel interface { 85 | Next() (interface{}, error) 86 | Close() 87 | } 88 | 89 | type watchChannel struct { 90 | dataCh chan interface{} 91 | closeCh chan struct{} 92 | } 93 | 94 | // ----------------------------------------------------------------------------- 95 | // Filter 96 | 97 | // newFilter creates a filter object, based on filter options and filter id. 98 | func newFilter(eth Eth, filterType FilterType, id uint64) Filter { 99 | return &baseFilter{ 100 | eth: eth, 101 | filterType: filterType, 102 | filterID: id, 103 | } 104 | } 105 | 106 | func (f *baseFilter) Watch() WatchChannel { 107 | dataCh := make(chan interface{}, dataBufferSize) 108 | closeCh := make(chan struct{}) 109 | var wg sync.WaitGroup 110 | wg.Add(1) 111 | 112 | go func(wg *sync.WaitGroup, closeCh <-chan struct{}, dataCh chan<- interface{}) { 113 | // TODO: configurable timer 114 | ticker := time.NewTicker(pollInterval) 115 | defer ticker.Stop() 116 | 117 | wg.Done() 118 | 119 | for { 120 | select { 121 | case <-closeCh: 122 | close(dataCh) 123 | return 124 | case <-ticker.C: 125 | results, _ := f.eth.GetFilterChanges(f) 126 | for _, r := range results { 127 | dataCh <- r 128 | } 129 | } 130 | } 131 | }(&wg, closeCh, dataCh) 132 | 133 | return &watchChannel{ 134 | dataCh: dataCh, 135 | closeCh: closeCh, 136 | } 137 | } 138 | 139 | // ID returns the filter identifier 140 | func (f *baseFilter) ID() uint64 { 141 | return f.filterID 142 | } 143 | 144 | // ----------------------------------------------------------------------------- 145 | // WatchChannel 146 | 147 | func (wc *watchChannel) Next() (interface{}, error) { 148 | if data, ok := <-wc.dataCh; ok { 149 | return data, nil 150 | } 151 | return nil, ErrChannelClosed 152 | } 153 | 154 | func (wc *watchChannel) Close() { 155 | wc.closeCh <- struct{}{} 156 | } 157 | -------------------------------------------------------------------------------- /rpc/jsonrpc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package rpc 31 | 32 | import ( 33 | "testing" 34 | 35 | "github.com/stretchr/testify/assert" 36 | "github.com/stretchr/testify/suite" 37 | ) 38 | 39 | type JSONRPCTestSuite struct { 40 | suite.Suite 41 | rpc RPC 42 | } 43 | 44 | func (suite *JSONRPCTestSuite) Test_Name() { 45 | rpc := suite.rpc 46 | assert.EqualValues(suite.T(), "jsonrpc", rpc.Name(), "Should be equal") 47 | } 48 | 49 | func (suite *JSONRPCTestSuite) Test_NewRequest() { 50 | rpc := suite.rpc 51 | req := rpc.NewRequest("test", "arg1", "arg2") 52 | 53 | if assert.NotNil(suite.T(), req) { 54 | assert.EqualValues(suite.T(), "2.0", req.Get("version").(string), "Should be equal") 55 | assert.EqualValues(suite.T(), []interface{}{"arg1", "arg2"}, req.Get("params").([]interface{}), "Should be equal") 56 | assert.EqualValues(suite.T(), "test", req.Get("method").(string), "Should be equal") 57 | assert.NotZero(suite.T(), req.ID(), "Should not be zero") 58 | assert.EqualValues(suite.T(), `{"jsonrpc":"2.0","method":"test","params":["arg1","arg2"],"id":1}`, req.String(), "Should be equal") 59 | 60 | req.Set("method", "new_method") 61 | assert.EqualValues(suite.T(), "new_method", req.Get("method").(string), "Should be equal") 62 | req.Set("params", "test_params") 63 | assert.EqualValues(suite.T(), []interface{}{"test_params"}, req.Get("params").([]interface{}), "Should be equal") 64 | req.Set("params", []string{"test_param1", "test_param2"}) 65 | assert.EqualValues(suite.T(), []interface{}{"test_param1", "test_param2"}, req.Get("params").([]interface{}), "Should be equal") 66 | } 67 | } 68 | 69 | func (suite *JSONRPCTestSuite) Test_NewResponse() { 70 | rpc := suite.rpc 71 | resp := rpc.NewResponse([]byte(`{"jsonrpc": "2.0", "id": 1, "result": ["result1", "result2"]}`)) 72 | if assert.NotNil(suite.T(), resp) { 73 | assert.EqualValues(suite.T(), "2.0", resp.Get("version").(string), "Should be equal") 74 | assert.EqualValues(suite.T(), 1, resp.ID(), "Should be equal") 75 | 76 | var results []string 77 | for _, r := range resp.Get("result").([]interface{}) { 78 | results = append(results, r.(string)) 79 | } 80 | assert.EqualValues(suite.T(), []string{"result1", "result2"}, results, "Should be equal") 81 | assert.EqualValues(suite.T(), `{"jsonrpc":"2.0","id":1,"result":["result1","result2"]}`, resp.String(), "Should be equal") 82 | } 83 | 84 | resp = rpc.NewResponse([]byte(`{"jsonrpc": "2.0", "id": 1, "result": ["result1", "result2"]}`)) 85 | if assert.NotNil(suite.T(), resp) { 86 | assert.EqualValues(suite.T(), "2.0", resp.Get("version").(string), "Should be equal") 87 | assert.EqualValues(suite.T(), 1, resp.ID(), "Should be equal") 88 | 89 | var results []string 90 | for _, r := range resp.Get("result").([]interface{}) { 91 | results = append(results, r.(string)) 92 | } 93 | assert.EqualValues(suite.T(), []string{"result1", "result2"}, results, "Should be equal") 94 | assert.Nil(suite.T(), resp.Get("xxx")) 95 | assert.EqualValues(suite.T(), `{"jsonrpc":"2.0","id":1,"result":["result1","result2"]}`, resp.String(), "Should be equal") 96 | } 97 | 98 | resp = rpc.NewResponse([]byte("xxx")) 99 | assert.Nil(suite.T(), resp) 100 | } 101 | 102 | func (suite *JSONRPCTestSuite) SetupTest() { 103 | suite.rpc = NewJSONRPC() 104 | } 105 | 106 | func (suite *JSONRPCTestSuite) TearDownTest() { 107 | 108 | } 109 | 110 | func Test_JSONRPCTestSuite(t *testing.T) { 111 | suite.Run(t, new(JSONRPCTestSuite)) 112 | } 113 | -------------------------------------------------------------------------------- /rpc/jsonrpc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package rpc 31 | 32 | import ( 33 | "encoding/json" 34 | "fmt" 35 | "math/big" 36 | "reflect" 37 | "strings" 38 | "sync/atomic" 39 | ) 40 | 41 | var ( 42 | big1 = big.NewInt(1) 43 | version = "2.0" 44 | ) 45 | 46 | // JSONRPCRequest ... 47 | type JSONRPCRequest struct { 48 | Version string `json:"jsonrpc,omitempty"` 49 | Method string `json:"method"` 50 | Params []interface{} `json:"params"` 51 | Identifier uint64 `json:"id"` 52 | } 53 | 54 | // Set ... 55 | func (req *JSONRPCRequest) Set(key string, value interface{}) { 56 | k := strings.ToLower(key) 57 | switch k { 58 | case "method": 59 | req.Method = fmt.Sprintf("%v", value) 60 | case "params": 61 | req.Params = req.Params[:0] 62 | switch reflect.TypeOf(value).Kind() { 63 | case reflect.Slice, reflect.Array: 64 | v := reflect.ValueOf(value) 65 | for i := 0; i < v.Len(); i++ { 66 | req.Params = append(req.Params, v.Index(i).String()) 67 | } 68 | default: 69 | req.Params = append(req.Params, value) 70 | } 71 | } 72 | } 73 | 74 | // Get ... 75 | func (req *JSONRPCRequest) Get(key string) interface{} { 76 | k := strings.ToLower(key) 77 | switch k { 78 | case "version": 79 | return req.Version 80 | case "method": 81 | return req.Method 82 | case "params": 83 | return req.Params 84 | case "id": 85 | return req.Identifier 86 | } 87 | 88 | return nil 89 | } 90 | 91 | // String ... 92 | func (req *JSONRPCRequest) String() string { 93 | jsonBytes, _ := json.Marshal(req) 94 | return string(jsonBytes) 95 | } 96 | 97 | // ID ... 98 | func (req *JSONRPCRequest) ID() uint64 { 99 | return req.Identifier 100 | } 101 | 102 | // ----------------------------------------------------------------------------- 103 | 104 | // JSONRPCError ... 105 | type JSONRPCError struct { 106 | Code int64 `json:"code"` 107 | Message string `json:"message"` 108 | } 109 | 110 | func (err *JSONRPCError) Error() string { 111 | return err.Message 112 | } 113 | 114 | // JSONRPCResponse ... 115 | type JSONRPCResponse struct { 116 | Version string `json:"jsonrpc"` 117 | Identifier uint64 `json:"id"` 118 | Result interface{} `json:"result"` 119 | Err *JSONRPCError `json:"error,omitempty"` 120 | } 121 | 122 | // Get ... 123 | func (resp *JSONRPCResponse) Get(key string) interface{} { 124 | k := strings.ToLower(key) 125 | switch k { 126 | case "version": 127 | return resp.Version 128 | case "id": 129 | return resp.Identifier 130 | case "result": 131 | return resp.Result 132 | case "error": 133 | return resp.Err 134 | } 135 | 136 | return nil 137 | } 138 | 139 | // String ... 140 | func (resp *JSONRPCResponse) String() string { 141 | jsonBytes, _ := json.Marshal(resp) 142 | return string(jsonBytes) 143 | } 144 | 145 | // ID ... 146 | func (resp *JSONRPCResponse) ID() uint64 { 147 | return resp.Identifier 148 | } 149 | 150 | func (resp *JSONRPCResponse) Error() error { 151 | if resp.Err != nil && resp.Err.Code != 0 { 152 | return resp.Err 153 | } 154 | return nil 155 | } 156 | 157 | // ----------------------------------------------------------------------------- 158 | 159 | // JSONRPC ... 160 | type JSONRPC struct { 161 | messageID uint64 162 | } 163 | 164 | // NewJSONRPC ... 165 | func NewJSONRPC() RPC { 166 | return &JSONRPC{messageID: 0} 167 | } 168 | 169 | // Name ... 170 | func (rpc *JSONRPC) Name() string { 171 | return "jsonrpc" 172 | } 173 | 174 | // NewRequest ... 175 | func (rpc *JSONRPC) NewRequest(method string, args ...interface{}) Request { 176 | request := &JSONRPCRequest{Version: version, Method: method, Identifier: rpc.newID()} 177 | request.Params = make([]interface{}, 0) 178 | for _, arg := range args { 179 | request.Params = append(request.Params, fmt.Sprintf("%v", arg)) 180 | } 181 | return request 182 | } 183 | 184 | // NewResponse ... 185 | func (rpc *JSONRPC) NewResponse(data []byte) Response { 186 | resp := &JSONRPCResponse{} 187 | if err := json.Unmarshal(data, &resp); err == nil { 188 | return resp 189 | } 190 | 191 | return nil 192 | } 193 | 194 | func (rpc *JSONRPC) newID() uint64 { 195 | return atomic.AddUint64(&rpc.messageID, 1) 196 | } 197 | -------------------------------------------------------------------------------- /common/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package common 31 | 32 | import ( 33 | "encoding/json" 34 | "math/big" 35 | ) 36 | 37 | const ( 38 | hashLength = 32 39 | addressLength = 20 40 | ) 41 | 42 | // Hash ... 43 | type Hash [hashLength]byte 44 | 45 | func NewHash(data []byte) (result Hash) { 46 | copy(result[:], data) 47 | return result 48 | } 49 | 50 | func (hash *Hash) String() string { 51 | return BytesToHex(hash[:]) 52 | } 53 | 54 | // Address ... 55 | type Address [addressLength]byte 56 | 57 | func NewAddress(data []byte) (result Address) { 58 | copy(result[:], data) 59 | return result 60 | } 61 | 62 | func (addr *Address) String() string { 63 | return BytesToHex(addr[:]) 64 | } 65 | 66 | // SyncStatus ... 67 | type SyncStatus struct { 68 | Result bool 69 | StartingBlock *big.Int 70 | CurrentBlock *big.Int 71 | HighestBlock *big.Int 72 | } 73 | 74 | // TransactionRequest ... 75 | type TransactionRequest struct { 76 | From Address `json:"from"` 77 | To Address `json:"to"` 78 | Gas *big.Int `json:"gas"` 79 | GasPrice *big.Int `json:"gasprice"` 80 | Value *big.Int `json:"value"` 81 | Data []byte `json:"data"` 82 | } 83 | 84 | func (tx *TransactionRequest) String() string { 85 | jsonBytes, _ := json.Marshal(tx) 86 | return string(jsonBytes) 87 | } 88 | 89 | // Transaction ... 90 | type Transaction struct { 91 | Hash Hash `json:"hash"` 92 | Nonce Hash `json:"nonce"` 93 | BlockHash Hash `json:"blockHash"` 94 | BlockNumber *big.Int `json:"blockNumber"` 95 | TransactionIndex uint64 `json:"transactionIndex"` 96 | From Address `json:"from"` 97 | To Address `json:"to"` 98 | Gas *big.Int `json:"gas"` 99 | GasPrice *big.Int `json:"gasprice"` 100 | Value *big.Int `json:"value"` 101 | Data []byte `json:"input"` 102 | } 103 | 104 | func (tx *Transaction) String() string { 105 | jsonBytes, _ := json.Marshal(tx) 106 | return string(jsonBytes) 107 | } 108 | 109 | type Topic struct { 110 | Data []byte 111 | } 112 | 113 | type Topics []Topic 114 | 115 | // Log ... 116 | type Log struct { 117 | LogIndex uint64 `json:"logIndex"` 118 | BlockNumber *big.Int `json:"blockNumber"` 119 | BlockHash Hash `json:"blockHash"` 120 | TransactionHash Hash `json:"transactionHash"` 121 | TransactionIndex uint64 `json:"transactionIndex"` 122 | Address Address `json:"address"` 123 | Data []byte `json:"data"` 124 | Topics Topics `json:"topics"` 125 | } 126 | 127 | // TransactionReceipt ... 128 | type TransactionReceipt struct { 129 | Hash Hash `json:"transactionHash"` 130 | TransactionIndex uint64 `json:"transactionIndex"` 131 | BlockNumber *big.Int `json:"blockNumber"` 132 | BlockHash Hash `json:"blockHash"` 133 | CumulativeGasUsed *big.Int `json:"cumulativeGasUsed"` 134 | GasUsed *big.Int `json:"gasUsed"` 135 | ContractAddress Address `json:"contractAddress"` 136 | Logs []Log `json:"logs"` 137 | } 138 | 139 | func (tx *TransactionReceipt) String() string { 140 | jsonBytes, _ := json.Marshal(tx) 141 | return string(jsonBytes) 142 | } 143 | 144 | // Block ... 145 | type Block struct { 146 | Number *big.Int `json:"number"` 147 | Hash Hash `json:"hash"` 148 | ParentHash Hash `json:"parentHash"` 149 | Nonce Hash `json:"nonce"` 150 | Sha3Uncles Hash `json:"sha3Uncles"` 151 | Bloom Hash `json:"logsBloom"` 152 | TransactionRoot Hash `json:"transactionsRoot"` 153 | StateRoot Hash `json:"stateRoot"` 154 | Miner Address `json:"miner"` 155 | Difficulty *big.Int `json:"difficulty"` 156 | TotalDifficulty *big.Int `json:"totalDifficulty"` 157 | ExtraData Hash `json:"extraData"` 158 | Size *big.Int `json:"size"` 159 | GasLimit *big.Int `json:"gasLimit"` 160 | GasUsed *big.Int `json:"gasUsed"` 161 | Timestamp *big.Int `json:"timestamp"` 162 | Transactions []Hash `json:"transactions"` 163 | Uncles []Hash `json:"uncles"` 164 | //MinGasPrice *big.Int `json:"minGasPrice"` 165 | } 166 | -------------------------------------------------------------------------------- /web3/web3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "math/big" 34 | "testing" 35 | 36 | "github.com/alanchchen/web3go/test" 37 | "github.com/stretchr/testify/assert" 38 | "github.com/stretchr/testify/suite" 39 | ) 40 | 41 | type Web3TestSuite struct { 42 | suite.Suite 43 | web3 *Web3 44 | } 45 | 46 | func (suite *Web3TestSuite) Test_IsConnected() { 47 | web3 := suite.web3 48 | assert.Equal(suite.T(), web3.IsConnected(), true, "should be true") 49 | } 50 | 51 | func (suite *Web3TestSuite) Test_Sha3() { 52 | web3 := suite.web3 53 | s := "Some string to be hashed" 54 | hash := web3.Sha3(s, nil) 55 | assert.Equal(suite.T(), "0xed973b234cf2238052c9ac87072c71bcf33abc1bbd721018e0cca448ef79b379", hash, "should be equal") 56 | 57 | encoding := struct { 58 | Encoding string `json:"encoding"` 59 | }{ 60 | Encoding: "hex", 61 | } 62 | assert.Equal(suite.T(), "0x85dd39c91a64167ba20732b228251e67caed1462d4bcf036af88dc6856d0fdcc", web3.Sha3(hash, encoding), "should be equal") 63 | } 64 | 65 | func (suite *Web3TestSuite) Test_ToHex() { 66 | web3 := suite.web3 67 | s := web3.ToHex("{\"test\":\"test\"}") 68 | assert.Equal(suite.T(), "0x7b2274657374223a2274657374227d", s, "should be equal") 69 | 70 | d := struct { 71 | Test string `json:"test"` 72 | }{ 73 | Test: "test", 74 | } 75 | s = web3.ToHex(d) 76 | assert.Equal(suite.T(), "0x7b2274657374223a2274657374227d", s, "should be equal") 77 | 78 | s = web3.ToHex(true) 79 | assert.Equal(suite.T(), "0x1", s, "should be equal") 80 | 81 | s = web3.ToHex(false) 82 | assert.Equal(suite.T(), "0x0", s, "should be equal") 83 | 84 | i := new(big.Int) 85 | i.SetInt64(12345) 86 | s = web3.ToHex(i) 87 | assert.Equal(suite.T(), "0x3039", s, "should be equal") 88 | } 89 | 90 | func (suite *Web3TestSuite) Test_ToASCII() { 91 | web3 := suite.web3 92 | s := "0x657468657265756d000000000000000000000000000000000000000000000000" 93 | assert.Equal(suite.T(), "ethereum", web3.ToASCII(s), "should be equal") 94 | } 95 | 96 | func (suite *Web3TestSuite) Test_FromASCII() { 97 | web3 := suite.web3 98 | s := "ethereum" 99 | assert.Equal(suite.T(), "0x657468657265756d", web3.FromASCII(s, 0), "should be equal") 100 | assert.Equal(suite.T(), "0x657468657265756d000000000000000000000000000000000000000000000000", web3.FromASCII(s, 32), "should be equal") 101 | } 102 | 103 | func (suite *Web3TestSuite) Test_ToDecimal() { 104 | web3 := suite.web3 105 | s := "0x15" 106 | assert.Equal(suite.T(), "21", web3.ToDecimal(s), "should be equal") 107 | } 108 | 109 | func (suite *Web3TestSuite) Test_FromDecimal() { 110 | web3 := suite.web3 111 | s := "21" 112 | assert.Equal(suite.T(), "0x15", web3.FromDecimal(s), "should be equal") 113 | } 114 | 115 | func (suite *Web3TestSuite) Test_FromWei() { 116 | web3 := suite.web3 117 | s := "1" 118 | assert.Equal(suite.T(), "1", web3.FromWei(s, "wei"), "should be equal") 119 | s = "1000000" 120 | assert.Equal(suite.T(), "1000", web3.FromWei(s, "kwei"), "should be equal") 121 | s = "999000000000" 122 | assert.Equal(suite.T(), "999", web3.FromWei(s, "gwei"), "should be equal") 123 | s = "123000000000000000000" 124 | assert.Equal(suite.T(), "123", web3.FromWei(s, "ether"), "should be equal") 125 | s = "1000000000000000000000000000000" 126 | assert.Equal(suite.T(), "1", web3.FromWei(s, "tether"), "should be equal") 127 | } 128 | 129 | func (suite *Web3TestSuite) Test_ToWei() { 130 | web3 := suite.web3 131 | s := "100000" 132 | assert.Equal(suite.T(), "0", web3.ToWei(s, "noether"), "should be equal") 133 | s = "1" 134 | assert.Equal(suite.T(), "1", web3.ToWei(s, "wei"), "should be equal") 135 | s = "1000" 136 | assert.Equal(suite.T(), "1000000", web3.ToWei(s, "kwei"), "should be equal") 137 | s = "999" 138 | assert.Equal(suite.T(), "999000000000", web3.ToWei(s, "gwei"), "should be equal") 139 | s = "123" 140 | assert.Equal(suite.T(), "123000000000000000000", web3.ToWei(s, "ether"), "should be equal") 141 | s = "1" 142 | assert.Equal(suite.T(), "1000000000000000000000000000000", web3.ToWei(s, "tether"), "should be equal") 143 | } 144 | 145 | func (suite *Web3TestSuite) Test_ToBigNumber() { 146 | web3 := suite.web3 147 | s := "200000000000000000000001" 148 | f64s, _ := web3.ToBigNumber(s).Float64() 149 | assert.Equal(suite.T(), 2.0000000000000002e+23, f64s, "should be equal") 150 | } 151 | 152 | func (suite *Web3TestSuite) Test_IsAddress() { 153 | web3 := suite.web3 154 | s := "0x1a9afb711302c5f83b5902843d1c007a1a137632" 155 | assert.Equal(suite.T(), true, web3.IsAddress(s), "should be equal") 156 | s = "0x26c7ea56af25113f712befbf2077798fd7fbdb7c" 157 | assert.Equal(suite.T(), true, web3.IsAddress(s), "should be equal") 158 | s = "0xa4137d4ad166ae825f1b8dbb0c3d48f25f172e9e" 159 | assert.Equal(suite.T(), true, web3.IsAddress(s), "should be equal") 160 | s = "0xA4137D4AD166AE825F1B8DBB0C3D48F25F172E9E" 161 | assert.Equal(suite.T(), true, web3.IsAddress(s), "should be equal") 162 | s = "0x1" 163 | assert.Equal(suite.T(), false, web3.IsAddress(s), "should be equal") 164 | s = "0xA4137d4ad166ae825f1b8dbb0c3d48f25f172e9e" 165 | assert.Equal(suite.T(), false, web3.IsAddress(s), "should be equal") 166 | s = "I'm an account" 167 | assert.Equal(suite.T(), false, web3.IsAddress(s), "should be equal") 168 | } 169 | 170 | func (suite *Web3TestSuite) SetupTest() { 171 | suite.web3 = NewWeb3(test.NewMockHTTPProvider()) 172 | } 173 | 174 | func Test_Web3TestSuite(t *testing.T) { 175 | suite.Run(t, new(Web3TestSuite)) 176 | } 177 | -------------------------------------------------------------------------------- /web3/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "encoding/json" 34 | "math/big" 35 | 36 | "github.com/alanchchen/web3go/common" 37 | ) 38 | 39 | type jsonBlock struct { 40 | Number json.Number `json:"number"` 41 | Hash common.Hash `json:"hash"` 42 | ParentHash common.Hash `json:"parentHash"` 43 | Nonce common.Hash `json:"nonce"` 44 | Sha3Uncles common.Hash `json:"sha3Uncles"` 45 | Bloom common.Hash `json:"logsBloom"` 46 | TransactionRoot common.Hash `json:"transactionsRoot"` 47 | StateRoot common.Hash `json:"stateRoot"` 48 | Miner common.Address `json:"miner"` 49 | Difficulty json.Number `json:"difficulty"` 50 | TotalDifficulty json.Number `json:"totalDifficulty"` 51 | ExtraData common.Hash `json:"extraData"` 52 | Size json.Number `json:"size"` 53 | GasLimit json.Number `json:"gasLimit"` 54 | GasUsed json.Number `json:"gasUsed"` 55 | Timestamp json.Number `json:"timestamp"` 56 | Transactions []common.Hash `json:"transactions"` 57 | Uncles []common.Hash `json:"uncles"` 58 | } 59 | 60 | func (b *jsonBlock) ToBlock() (block *common.Block) { 61 | block = &common.Block{} 62 | block.Number = jsonNumbertoInt(b.Number) 63 | block.Hash = b.Hash 64 | block.ParentHash = b.ParentHash 65 | block.Nonce = b.Nonce 66 | block.Sha3Uncles = b.Sha3Uncles 67 | block.Bloom = b.Bloom 68 | block.TransactionRoot = b.TransactionRoot 69 | block.StateRoot = b.StateRoot 70 | block.Miner = b.Miner 71 | block.Difficulty = jsonNumbertoInt(b.Difficulty) 72 | block.TotalDifficulty = jsonNumbertoInt(b.TotalDifficulty) 73 | block.ExtraData = b.ExtraData 74 | block.Size = jsonNumbertoInt(b.Size) 75 | block.GasLimit = jsonNumbertoInt(b.GasLimit) 76 | block.GasUsed = jsonNumbertoInt(b.GasUsed) 77 | block.Timestamp = jsonNumbertoInt(b.Timestamp) 78 | block.Transactions = b.Transactions 79 | block.Uncles = b.Uncles 80 | return block 81 | } 82 | 83 | type jsonTransaction struct { 84 | Hash common.Hash `json:"hash"` 85 | Nonce common.Hash `json:"nonce"` 86 | BlockHash common.Hash `json:"blockHash"` 87 | BlockNumber json.Number `json:"blockNumber"` 88 | TransactionIndex uint64 `json:"transactionIndex"` 89 | From common.Address `json:"from"` 90 | To common.Address `json:"to"` 91 | Gas json.Number `json:"gas"` 92 | GasPrice json.Number `json:"gasprice"` 93 | Value json.Number `json:"value"` 94 | Data []byte `json:"input"` 95 | } 96 | 97 | func (t *jsonTransaction) ToTransaction() (tx *common.Transaction) { 98 | tx = &common.Transaction{} 99 | tx.Hash = t.Hash 100 | tx.Nonce = t.Nonce 101 | tx.BlockHash = t.BlockHash 102 | tx.BlockNumber = jsonNumbertoInt(t.BlockNumber) 103 | tx.TransactionIndex = t.TransactionIndex 104 | tx.From = t.From 105 | tx.To = t.To 106 | tx.Gas = jsonNumbertoInt(t.Gas) 107 | tx.GasPrice = jsonNumbertoInt(t.GasPrice) 108 | tx.Value = jsonNumbertoInt(t.Value) 109 | tx.Data = t.Data 110 | return tx 111 | } 112 | 113 | type jsonTransactionReceipt struct { 114 | Hash common.Hash `json:"transactionHash"` 115 | TransactionIndex uint64 `json:"transactionIndex"` 116 | BlockNumber json.Number `json:"blockNumber"` 117 | BlockHash common.Hash `json:"blockHash"` 118 | CumulativeGasUsed json.Number `json:"cumulativeGasUsed"` 119 | GasUsed json.Number `json:"gasUsed"` 120 | ContractAddress common.Address `json:"contractAddress"` 121 | Logs []jsonLog `json:"logs"` 122 | } 123 | 124 | func (r *jsonTransactionReceipt) ToTransactionReceipt() (receipt *common.TransactionReceipt) { 125 | receipt = &common.TransactionReceipt{} 126 | receipt.Hash = r.Hash 127 | receipt.TransactionIndex = r.TransactionIndex 128 | receipt.BlockNumber = jsonNumbertoInt(r.BlockNumber) 129 | receipt.BlockHash = r.BlockHash 130 | receipt.CumulativeGasUsed = jsonNumbertoInt(r.CumulativeGasUsed) 131 | receipt.GasUsed = jsonNumbertoInt(r.GasUsed) 132 | receipt.ContractAddress = r.ContractAddress 133 | receipt.Logs = make([]common.Log, 0) 134 | for _, l := range r.Logs { 135 | receipt.Logs = append(receipt.Logs, l.ToLog()) 136 | } 137 | return receipt 138 | } 139 | 140 | type jsonLog struct { 141 | LogIndex uint64 `json:"logIndex"` 142 | BlockNumber json.Number `json:"blockNumber"` 143 | BlockHash common.Hash `json:"blockHash"` 144 | TransactionHash common.Hash `json:"transactionHash"` 145 | TransactionIndex uint64 `json:"transactionIndex"` 146 | Address common.Address `json:"address"` 147 | Data []byte `json:"data"` 148 | Topics common.Topics `json:"topics"` 149 | } 150 | 151 | func (l jsonLog) ToLog() (log common.Log) { 152 | log = common.Log{} 153 | log.LogIndex = l.LogIndex 154 | log.BlockNumber = jsonNumbertoInt(l.BlockNumber) 155 | log.BlockHash = l.BlockHash 156 | log.TransactionHash = l.TransactionHash 157 | log.TransactionIndex = l.TransactionIndex 158 | log.Address = l.Address 159 | log.Data = l.Data 160 | log.Topics = l.Topics 161 | return log 162 | } 163 | 164 | func jsonNumbertoInt(data json.Number) *big.Int { 165 | f := big.NewFloat(0.0) 166 | f.SetString(string(data)) 167 | result, _ := f.Int(nil) 168 | return result 169 | } 170 | -------------------------------------------------------------------------------- /web3/web3.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "bytes" 34 | "encoding/json" 35 | "fmt" 36 | "math" 37 | "math/big" 38 | "regexp" 39 | "strconv" 40 | "strings" 41 | 42 | "github.com/alanchchen/web3go/common" 43 | "github.com/alanchchen/web3go/provider" 44 | "github.com/tonnerre/golang-go.crypto/sha3" 45 | ) 46 | 47 | var ( 48 | big0 = big.NewInt(0) 49 | rat0 = big.NewRat(0, 1) 50 | unitMap = map[string]string{ 51 | "noether": "0", 52 | "wei": "1", 53 | "kwei": "1000", 54 | "Kwei": "1000", 55 | "babbage": "1000", 56 | "femtoether": "1000", 57 | "mwei": "1000000", 58 | "Mwei": "1000000", 59 | "lovelace": "1000000", 60 | "picoether": "1000000", 61 | "gwei": "1000000000", 62 | "Gwei": "1000000000", 63 | "shannon": "1000000000", 64 | "nanoether": "1000000000", 65 | "nano": "1000000000", 66 | "szabo": "1000000000000", 67 | "microether": "1000000000000", 68 | "micro": "1000000000000", 69 | "finney": "1000000000000000", 70 | "milliether": "1000000000000000", 71 | "milli": "1000000000000000", 72 | "ether": "1000000000000000000", 73 | "kether": "1000000000000000000000", 74 | "grand": "1000000000000000000000", 75 | "mether": "1000000000000000000000000", 76 | "gether": "1000000000000000000000000000", 77 | "tether": "1000000000000000000000000000000", 78 | } 79 | ) 80 | 81 | // Web3 Standard interface 82 | // See https://github.com/ethereum/wiki/wiki/JavaScript-API#web3js-api-reference 83 | type Web3 struct { 84 | provider provider.Provider 85 | requestManager *requestManager 86 | Eth Eth 87 | Net Net 88 | } 89 | 90 | // NewWeb3 creates a new web3 object. 91 | func NewWeb3(provider provider.Provider) *Web3 { 92 | requestManager := newRequestManager(provider) 93 | return &Web3{ 94 | provider: provider, 95 | requestManager: requestManager, 96 | Eth: newEthAPI(requestManager), 97 | Net: newNetAPI(requestManager)} 98 | } 99 | 100 | // IsConnected checks if a connection to a node exists. 101 | func (web3 *Web3) IsConnected() bool { 102 | return true 103 | } 104 | 105 | // SetProvider sets provider. 106 | func (web3 *Web3) SetProvider(provider provider.Provider) { 107 | web3.provider = provider 108 | } 109 | 110 | // CurrentProvider returns the current provider. 111 | func (web3 *Web3) CurrentProvider() provider.Provider { 112 | return web3.provider 113 | } 114 | 115 | // Reset state of web3. Resets everything except manager. Uninstalls all 116 | // filters. Stops polling. If keepSyncing is true, it will uninstall all 117 | // filters, but will keep the web3.eth.IsSyncing() polls. 118 | func (web3 *Web3) Reset(keepSyncing bool) { 119 | 120 | } 121 | 122 | // Sha3 returns Keccak-256 (not the standardized SHA3-256) of the given data. 123 | func (web3 *Web3) Sha3(data string, options interface{}) string { 124 | opt := struct { 125 | Encoding string `json:"encoding"` 126 | }{ 127 | "default", 128 | } 129 | 130 | checkEncoding: 131 | switch options.(type) { 132 | case string: 133 | if err := json.Unmarshal([]byte(options.(string)), &opt); err != nil { 134 | return common.BytesToHex(web3.sha3Hash([]byte(data))) 135 | } 136 | break checkEncoding 137 | default: 138 | var err error 139 | var optBytes []byte 140 | if optBytes, err = json.Marshal(options); err != nil { 141 | return common.BytesToHex(web3.sha3Hash([]byte(data))) 142 | } 143 | 144 | if err = json.Unmarshal(optBytes, &opt); err != nil { 145 | return common.BytesToHex(web3.sha3Hash([]byte(data))) 146 | } 147 | break checkEncoding 148 | } 149 | 150 | if opt.Encoding == "hex" { 151 | return common.BytesToHex(web3.sha3Hash(common.HexToBytes(data))) 152 | } 153 | return common.BytesToHex(web3.sha3Hash([]byte(data))) 154 | } 155 | 156 | // ToHex converts any value into HEX. 157 | func (web3 *Web3) ToHex(value interface{}) string { 158 | switch value.(type) { 159 | case bool: 160 | v := value.(bool) 161 | if v { 162 | return "0x1" 163 | } 164 | return "0x0" 165 | case string: 166 | jsonBytes, err := json.Marshal(value) 167 | if err != nil { 168 | return web3.FromDecimal(value) 169 | } 170 | unquoted, err := strconv.Unquote(string(jsonBytes)) 171 | if err != nil { 172 | return common.BytesToHex(jsonBytes) 173 | } 174 | return common.BytesToHex([]byte(unquoted)) 175 | case *big.Int: 176 | return web3.FromDecimal(value) 177 | default: 178 | jsonBytes, err := json.Marshal(value) 179 | if err != nil { 180 | return web3.FromDecimal(value) 181 | } 182 | return common.BytesToHex(jsonBytes) 183 | } 184 | } 185 | 186 | // ToASCII converts a HEX string into a ASCII string. 187 | func (web3 *Web3) ToASCII(hexString string) string { 188 | return string(bytes.Trim(common.HexToBytes(hexString), "\x00")) 189 | } 190 | 191 | // FromASCII converts any ASCII string to a HEX string. 192 | func (web3 *Web3) FromASCII(textString string, padding int) string { 193 | hex := "" 194 | for _, runeValue := range textString { 195 | hex += fmt.Sprintf("%x", runeValue) 196 | } 197 | 198 | l := len(hex) 199 | for i := 0; i < padding*2-l; i++ { 200 | hex += "0" 201 | } 202 | return "0x" + hex 203 | } 204 | 205 | // ToDecimal converts value to it"s decimal representation in string. 206 | func (web3 *Web3) ToDecimal(value interface{}) string { 207 | n := web3.ToBigNumber(value) 208 | if n.IsInt() { 209 | return n.Num().String() 210 | } 211 | return n.String() 212 | } 213 | 214 | // FromDecimal converts value to it"s hex representation. 215 | func (web3 *Web3) FromDecimal(value interface{}) string { 216 | number := web3.ToBigNumber(value) 217 | if number.IsInt() { 218 | result := number.Num().Text(16) 219 | 220 | if number.Cmp(rat0) < 0 { 221 | return "-0x" + result[1:] 222 | } 223 | return "0x" + result 224 | } 225 | 226 | v, _ := number.Float64() 227 | return fmt.Sprintf("%x", math.Float64bits(v)) 228 | } 229 | 230 | // FromWei takes a number of wei and converts it to any other ether unit. 231 | // 232 | // Possible units are: 233 | // SI Short SI Full Effigy Other 234 | // - kwei femtoether babbage 235 | // - mwei picoether lovelace 236 | // - gwei nanoether shannon nano 237 | // - -- microether szabo micro 238 | // - -- microether szabo micro 239 | // - -- milliether finney milli 240 | // - ether -- -- 241 | // - kether -- grand 242 | // - mether 243 | // - gether 244 | // - tether 245 | func (web3 *Web3) FromWei(number string, unit string) string { 246 | num := web3.ToBigNumber(number) 247 | returnValue := num.Quo(num, web3.getValueOfUnit(unit)) 248 | return returnValue.Num().String() 249 | } 250 | 251 | // ToWei takes a number of a unit and converts it to wei. 252 | // 253 | // Possible units are: 254 | // SI Short SI Full Effigy Other 255 | // - kwei femtoether babbage 256 | // - mwei picoether lovelace 257 | // - gwei nanoether shannon nano 258 | // - -- microether szabo micro 259 | // - -- microether szabo micro 260 | // - -- milliether finney milli 261 | // - ether -- -- 262 | // - kether -- grand 263 | // - mether 264 | // - gether 265 | // - tether 266 | func (web3 *Web3) ToWei(number interface{}, unit string) string { 267 | num := web3.ToBigNumber(number) 268 | returnValue := num.Mul(num, web3.getValueOfUnit(unit)) 269 | return returnValue.Num().String() 270 | } 271 | 272 | // ToBigNumber takes an input and transforms it into an *big.Rat. 273 | func (web3 *Web3) ToBigNumber(value interface{}) (result *big.Rat) { 274 | switch value.(type) { 275 | case *big.Rat: 276 | v := value.(*big.Rat) 277 | return v 278 | case *big.Int: 279 | v := value.(*big.Int) 280 | result = new(big.Rat) 281 | result.SetInt(v) 282 | return result 283 | case string: 284 | v := value.(string) 285 | i := new(big.Int) 286 | result = new(big.Rat) 287 | 288 | if strings.Index(v, "0x") == 0 || strings.Index(v, "-0x") == 0 { 289 | i.SetString(strings.Replace(v, "0x", "", -1), 16) 290 | } else { 291 | i.SetString(v, 10) 292 | } 293 | result.SetInt(i) 294 | return result 295 | } 296 | return result 297 | } 298 | 299 | // IsAddress checks if the given string is an address. 300 | func (web3 *Web3) IsAddress(address string) bool { 301 | smallCapsMatcher := regexp.MustCompile("^(0x)?[0-9a-f]{40}$") 302 | smallCapsMatched := smallCapsMatcher.MatchString(address) 303 | allCapsMatcher := regexp.MustCompile("^(0x)?[0-9A-F]{40}$") 304 | allCapsMatched := allCapsMatcher.MatchString(address) 305 | if smallCapsMatched || allCapsMatched { 306 | return true 307 | } 308 | return web3.isChecksumAddress(address) 309 | } 310 | 311 | func (web3 *Web3) isChecksumAddress(address string) bool { 312 | addr := strings.Replace(address, "0x", "", -1) 313 | addressHash := web3.Sha3(strings.ToLower(addr), "") 314 | 315 | for i := 0; i < 40; i++ { 316 | d, err := strconv.ParseInt(string(addressHash[i]), 16, 32) 317 | if err != nil { 318 | return false 319 | } 320 | 321 | if d > 7 && strings.ToUpper(string(address[i])) == string(address[i]) || 322 | d <= 7 && strings.ToLower(string(address[i])) == string(address[i]) { 323 | return false 324 | } 325 | } 326 | return true 327 | } 328 | 329 | func (web3 *Web3) sha3Hash(data ...[]byte) []byte { 330 | d := sha3.NewKeccak256() 331 | for _, b := range data { 332 | d.Write(b) 333 | } 334 | return d.Sum(nil) 335 | } 336 | 337 | func (web3 *Web3) getValueOfUnit(unit string) *big.Rat { 338 | u := strings.TrimSpace(unit) 339 | if u != "" { 340 | u = strings.ToLower(u) 341 | } else { 342 | u = "ether" 343 | } 344 | 345 | if unitValue, ok := unitMap[u]; ok { 346 | value := new(big.Int) 347 | value.SetString(unitValue, 10) 348 | returnValue := new(big.Rat) 349 | returnValue.SetInt(value) 350 | return returnValue 351 | } 352 | 353 | keys := make([]string, 0, len(unitMap)) 354 | for k := range unitMap { 355 | keys = append(keys, k) 356 | } 357 | panic(fmt.Sprintf("This unit doesn\"t exists, please use the one of the following units, %v", keys)) 358 | } 359 | -------------------------------------------------------------------------------- /test/mock_eth.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package test 31 | 32 | import ( 33 | "fmt" 34 | "math/big" 35 | 36 | "github.com/alanchchen/web3go/common" 37 | "github.com/alanchchen/web3go/rpc" 38 | ) 39 | 40 | // MockEthAPI ... 41 | type MockEthAPI struct { 42 | rpc rpc.RPC 43 | } 44 | 45 | // NewMockEthAPI ... 46 | func NewMockEthAPI(rpc rpc.RPC) MockAPI { 47 | return &MockEthAPI{rpc: rpc} 48 | } 49 | 50 | // Do ... 51 | func (eth *MockEthAPI) Do(request rpc.Request) (response rpc.Response, err error) { 52 | method := request.Get("method").(string) 53 | switch method { 54 | case "eth_protocolVersion": 55 | return generateResponse(eth.rpc, request, "54") 56 | case "eth_syncing": 57 | return generateResponse(eth.rpc, request, false) 58 | case "eth_coinbase": 59 | return generateResponse(eth.rpc, request, "0x407d73d8a49eeb85d32cf465507dd71d507100c1") 60 | case "eth_mining": 61 | return generateResponse(eth.rpc, request, true) 62 | case "eth_hashrate": 63 | return generateResponse(eth.rpc, request, "0x38a") 64 | case "eth_gasPrice": 65 | return generateResponse(eth.rpc, request, "0x09184e72a000") 66 | case "eth_accounts": 67 | return generateResponse(eth.rpc, request, 68 | []string{"0x407d73d8a49eeb85d32cf465507dd71d507100c1", 69 | "0x407d73d8a49ee783afd32cf465507dd71d507100"}) 70 | case "eth_blockNumber": 71 | return generateResponse(eth.rpc, request, "0x4b7") 72 | case "eth_getBalance": 73 | return generateResponse(eth.rpc, request, "0x0234c8a3397aab58") 74 | case "eth_getStorageAt": 75 | return generateResponse(eth.rpc, request, "0x03") 76 | case "eth_getTransactionCount": 77 | return generateResponse(eth.rpc, request, "0x1") 78 | case "eth_getBlockTransactionCountByHash": 79 | return generateResponse(eth.rpc, request, "0xb") 80 | case "eth_getBlockTransactionCountByNumber": 81 | return generateResponse(eth.rpc, request, "0xa") 82 | case "eth_getUncleCountByBlockHash": 83 | return generateResponse(eth.rpc, request, "0x1") 84 | case "eth_getUncleCountByBlockNumber": 85 | return generateResponse(eth.rpc, request, "0x1") 86 | case "eth_getCode": 87 | return generateResponse(eth.rpc, request, "0x600160008035811a818181146012578301005b601b6001356025565b8060005260206000f25b600060078202905091905056") 88 | case "eth_sign": 89 | return generateResponse(eth.rpc, request, "0x2ac19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601") 90 | case "eth_sendTransaction": 91 | return generateResponse(eth.rpc, request, "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331") 92 | case "eth_sendRawTransaction": 93 | return generateResponse(eth.rpc, request, "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331") 94 | case "eth_call": 95 | return generateResponse(eth.rpc, request, "0x") 96 | case "eth_estimateGas": 97 | return generateResponse(eth.rpc, request, "0x5208") 98 | case "eth_getBlockByHash": 99 | block := &common.Block{ 100 | Number: big.NewInt(0x1b4), 101 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 102 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 103 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 104 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 105 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 106 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 107 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 108 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 109 | Difficulty: big.NewInt(0x027f07), 110 | TotalDifficulty: big.NewInt(0x027f07), 111 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 112 | Size: big.NewInt(0x027f07), 113 | GasLimit: big.NewInt(0x9f759), 114 | GasUsed: big.NewInt(0x9f759), 115 | Timestamp: big.NewInt(0x54e34e8e), 116 | Transactions: []common.Hash{}, 117 | Uncles: []common.Hash{}, 118 | } 119 | return generateResponse(eth.rpc, request, block) 120 | case "eth_getBlockByNumber": 121 | block := &common.Block{ 122 | Number: big.NewInt(0x1b4), 123 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 124 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 125 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 126 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 127 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 128 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 129 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 130 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 131 | Difficulty: big.NewInt(0x027f07), 132 | TotalDifficulty: big.NewInt(0x027f07), 133 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 134 | Size: big.NewInt(0x027f07), 135 | GasLimit: big.NewInt(0x9f759), 136 | GasUsed: big.NewInt(0x9f759), 137 | Timestamp: big.NewInt(0x54e34e8e), 138 | Transactions: []common.Hash{}, 139 | Uncles: []common.Hash{}, 140 | } 141 | return generateResponse(eth.rpc, request, block) 142 | case "eth_getTransactionByHash": 143 | tx := &common.Transaction{ 144 | Hash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 145 | Nonce: common.NewHash(common.HexToBytes("0x")), 146 | BlockHash: common.NewHash(common.HexToBytes("0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b")), 147 | BlockNumber: big.NewInt(0x15df), 148 | TransactionIndex: 0x1, 149 | From: common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 150 | To: common.NewAddress(common.HexToBytes("0x85h43d8a49eeb85d32cf465507dd71d507100c1")), 151 | Value: big.NewInt(0x7f110), 152 | Gas: big.NewInt(0x7f110), 153 | GasPrice: big.NewInt(0x09184e72a000), 154 | Data: common.HexToBytes("0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"), 155 | } 156 | return generateResponse(eth.rpc, request, tx) 157 | case "eth_getTransactionByBlockHashAndIndex": 158 | tx := &common.Transaction{ 159 | Hash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 160 | Nonce: common.NewHash(common.HexToBytes("0x")), 161 | BlockHash: common.NewHash(common.HexToBytes("0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b")), 162 | BlockNumber: big.NewInt(0x15df), 163 | TransactionIndex: 0x1, 164 | From: common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 165 | To: common.NewAddress(common.HexToBytes("0x85h43d8a49eeb85d32cf465507dd71d507100c1")), 166 | Value: big.NewInt(0x7f110), 167 | Gas: big.NewInt(0x7f110), 168 | GasPrice: big.NewInt(0x09184e72a000), 169 | Data: common.HexToBytes("0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"), 170 | } 171 | return generateResponse(eth.rpc, request, tx) 172 | case "eth_getTransactionByBlockNumberAndIndex": 173 | tx := &common.Transaction{ 174 | Hash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 175 | Nonce: common.NewHash(common.HexToBytes("0x")), 176 | BlockHash: common.NewHash(common.HexToBytes("0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b")), 177 | BlockNumber: big.NewInt(0x15df), 178 | TransactionIndex: 0x1, 179 | From: common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 180 | To: common.NewAddress(common.HexToBytes("0x85h43d8a49eeb85d32cf465507dd71d507100c1")), 181 | Value: big.NewInt(0x7f110), 182 | Gas: big.NewInt(0x7f110), 183 | GasPrice: big.NewInt(0x09184e72a000), 184 | Data: common.HexToBytes("0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"), 185 | } 186 | return generateResponse(eth.rpc, request, tx) 187 | case "eth_getTransactionReceipt": 188 | receipt := &common.TransactionReceipt{ 189 | Hash: common.NewHash(common.HexToBytes("0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238")), 190 | TransactionIndex: 0x1, 191 | BlockNumber: big.NewInt(0xb), 192 | BlockHash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 193 | CumulativeGasUsed: big.NewInt(0x33bc), 194 | GasUsed: big.NewInt(0x4dc), 195 | ContractAddress: common.NewAddress(common.HexToBytes("0xb60e8dd61c5d32be8058bb8eb970870f07233155")), 196 | Logs: []common.Log{}, 197 | } 198 | return generateResponse(eth.rpc, request, receipt) 199 | case "eth_getUncleByBlockHashAndIndex": 200 | block := &common.Block{ 201 | Number: big.NewInt(0x1b4), 202 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 203 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 204 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 205 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 206 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 207 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 208 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 209 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 210 | Difficulty: big.NewInt(0x027f07), 211 | TotalDifficulty: big.NewInt(0x027f07), 212 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 213 | Size: big.NewInt(0x027f07), 214 | GasLimit: big.NewInt(0x9f759), 215 | GasUsed: big.NewInt(0x9f759), 216 | Timestamp: big.NewInt(0x54e34e8e), 217 | Transactions: []common.Hash{}, 218 | Uncles: []common.Hash{}, 219 | } 220 | return generateResponse(eth.rpc, request, block) 221 | case "eth_getUncleByBlockNumberAndIndex": 222 | block := &common.Block{ 223 | Number: big.NewInt(0x1b4), 224 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 225 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 226 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 227 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 228 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 229 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 230 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 231 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 232 | Difficulty: big.NewInt(0x027f07), 233 | TotalDifficulty: big.NewInt(0x027f07), 234 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 235 | Size: big.NewInt(0x027f07), 236 | GasLimit: big.NewInt(0x9f759), 237 | GasUsed: big.NewInt(0x9f759), 238 | Timestamp: big.NewInt(0x54e34e8e), 239 | Transactions: []common.Hash{}, 240 | Uncles: []common.Hash{}, 241 | } 242 | return generateResponse(eth.rpc, request, block) 243 | case "eth_getCompilers": 244 | return generateResponse(eth.rpc, request, []string{"solidity", "lll", "serpent"}) 245 | // case "eth_compileSolidity": 246 | // case "eth_compileLLL": 247 | // case "eth_compileSerpent": 248 | case "eth_newFilter": 249 | return generateResponse(eth.rpc, request, "0x1") 250 | case "eth_newBlockFilter": 251 | return generateResponse(eth.rpc, request, "0x1") 252 | case "eth_newPendingTransactionFilter": 253 | return generateResponse(eth.rpc, request, "0x1") 254 | case "eth_uninstallFilter": 255 | return generateResponse(eth.rpc, request, true) 256 | case "eth_getFilterChanges": 257 | logs := []common.Log{ 258 | { 259 | LogIndex: 0x1, 260 | BlockNumber: big.NewInt(0x1b4), 261 | BlockHash: common.NewHash(common.HexToBytes("0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 262 | TransactionHash: common.NewHash(common.HexToBytes("0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf")), 263 | TransactionIndex: 0, 264 | Address: common.NewAddress(common.HexToBytes("0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 265 | Data: []byte("0000000000000000000000000000000000000000000000000000000000000000"), 266 | Topics: common.Topics{ 267 | { 268 | Data: common.HexToBytes("0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5"), 269 | }, 270 | }, 271 | }, 272 | } 273 | return generateResponse(eth.rpc, request, logs) 274 | case "eth_getFilterLogs": 275 | logs := []common.Log{ 276 | { 277 | LogIndex: 0x1, 278 | BlockNumber: big.NewInt(0x1b4), 279 | BlockHash: common.NewHash(common.HexToBytes("0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 280 | TransactionHash: common.NewHash(common.HexToBytes("0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf")), 281 | TransactionIndex: 0, 282 | Address: common.NewAddress(common.HexToBytes("0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 283 | Data: []byte("0000000000000000000000000000000000000000000000000000000000000000"), 284 | Topics: common.Topics{ 285 | { 286 | Data: common.HexToBytes("0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5"), 287 | }, 288 | }, 289 | }, 290 | } 291 | return generateResponse(eth.rpc, request, logs) 292 | case "eth_getLogs": 293 | logs := []common.Log{ 294 | { 295 | LogIndex: 0x1, 296 | BlockNumber: big.NewInt(0x1b4), 297 | BlockHash: common.NewHash(common.HexToBytes("0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 298 | TransactionHash: common.NewHash(common.HexToBytes("0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf")), 299 | TransactionIndex: 0, 300 | Address: common.NewAddress(common.HexToBytes("0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 301 | Data: []byte("0000000000000000000000000000000000000000000000000000000000000000"), 302 | Topics: common.Topics{ 303 | { 304 | Data: common.HexToBytes("0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5"), 305 | }, 306 | }, 307 | }, 308 | } 309 | return generateResponse(eth.rpc, request, logs) 310 | case "eth_getWork": 311 | return generateResponse(eth.rpc, request, []string{ 312 | "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", 313 | "0x5EED00000000000000000000000000005EED0000000000000000000000000000", 314 | "0xd1ff1c01710000000000000000000000d1ff1c01710000000000000000000000"}) 315 | case "eth_submitWork": 316 | return generateResponse(eth.rpc, request, true) 317 | 318 | // case "eth_submitHashrate": 319 | } 320 | 321 | return nil, fmt.Errorf("Invalid method %s", method) 322 | } 323 | -------------------------------------------------------------------------------- /web3/eth_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "encoding/json" 34 | "math/big" 35 | "strings" 36 | "testing" 37 | 38 | "github.com/alanchchen/web3go/common" 39 | "github.com/alanchchen/web3go/test" 40 | "github.com/stretchr/testify/assert" 41 | "github.com/stretchr/testify/suite" 42 | ) 43 | 44 | type EthTestSuite struct { 45 | suite.Suite 46 | web3 *Web3 47 | eth Eth 48 | } 49 | 50 | func (suite *EthTestSuite) Test_ProcotolVersion() { 51 | eth := suite.eth 52 | result, err := eth.ProtocolVersion() 53 | assert.NoError(suite.T(), err, "Should be no error") 54 | assert.NotEqual(suite.T(), "", result, "version is empty") 55 | } 56 | 57 | func (suite *EthTestSuite) Test_Syncing() { 58 | eth := suite.eth 59 | status, err := eth.Syncing() 60 | assert.NoError(suite.T(), err, "Should be no error") 61 | assert.Exactly(suite.T(), false, status.Result, "should be false") 62 | } 63 | 64 | func (suite *EthTestSuite) Test_Coinbase() { 65 | eth := suite.eth 66 | address, err := eth.Coinbase() 67 | assert.NoError(suite.T(), err, "Should be no error") 68 | assert.EqualValues(suite.T(), "0x407d73d8a49eeb85d32cf465507dd71d507100c1", address.String(), "should be equal") 69 | } 70 | 71 | func (suite *EthTestSuite) Test_Mining() { 72 | eth := suite.eth 73 | mining, err := eth.Mining() 74 | assert.NoError(suite.T(), err, "Should be no error") 75 | assert.EqualValues(suite.T(), true, mining, "should be equal") 76 | } 77 | 78 | func (suite *EthTestSuite) Test_HashRate() { 79 | eth := suite.eth 80 | hashrate, err := eth.HashRate() 81 | assert.NoError(suite.T(), err, "Should be no error") 82 | assert.EqualValues(suite.T(), 0x38a, hashrate, "Should be equal") 83 | } 84 | 85 | func (suite *EthTestSuite) Test_GasPrice() { 86 | eth := suite.eth 87 | price, err := eth.GasPrice() 88 | assert.NoError(suite.T(), err, "Should be no error") 89 | assert.EqualValues(suite.T(), big.NewInt(0x09184e72a000), price, "Should be equal") 90 | } 91 | 92 | func (suite *EthTestSuite) Test_Accounts() { 93 | eth := suite.eth 94 | accounts, err := eth.Accounts() 95 | assert.NoError(suite.T(), err, "Should be no error") 96 | assert.EqualValues(suite.T(), []common.Address{ 97 | common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 98 | common.NewAddress(common.HexToBytes("0x407d73d8a49ee783afd32cf465507dd71d507100")), 99 | }, accounts, "Should be equal") 100 | } 101 | 102 | func (suite *EthTestSuite) Test_BlockNumber() { 103 | eth := suite.eth 104 | blockNumber, err := eth.BlockNumber() 105 | assert.NoError(suite.T(), err, "Should be no error") 106 | assert.EqualValues(suite.T(), big.NewInt(0x4b7), blockNumber, "Should be equal") 107 | } 108 | 109 | func (suite *EthTestSuite) Test_GetBalance() { 110 | eth := suite.eth 111 | balance, err := eth.GetBalance(common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), "latest") 112 | assert.NoError(suite.T(), err, "Should be no error") 113 | assert.EqualValues(suite.T(), 114 | big.NewInt(0x0234c8a3397aab58), 115 | balance, 116 | "Should be equal") 117 | } 118 | 119 | func (suite *EthTestSuite) Test_GetStorageAt() { 120 | eth := suite.eth 121 | storage, err := eth.GetStorageAt(common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 0, "latest") 122 | assert.NoError(suite.T(), err, "Should be no error") 123 | assert.EqualValues(suite.T(), 124 | 0x03, 125 | storage, 126 | "Should be equal") 127 | } 128 | 129 | func (suite *EthTestSuite) Test_GetTransactionCount() { 130 | eth := suite.eth 131 | transactionCount, err := eth.GetTransactionCount(common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), "latest") 132 | assert.NoError(suite.T(), err, "Should be no error") 133 | assert.EqualValues(suite.T(), 134 | big.NewInt(0x1), 135 | transactionCount, 136 | "Should be equal") 137 | } 138 | 139 | func (suite *EthTestSuite) Test_GetBlockTransactionCountByHash() { 140 | eth := suite.eth 141 | transactionCount, err := eth.GetBlockTransactionCountByHash(common.NewHash(common.HexToBytes("0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"))) 142 | assert.NoError(suite.T(), err, "Should be no error") 143 | assert.EqualValues(suite.T(), 144 | big.NewInt(0xb), 145 | transactionCount, 146 | "Should be equal") 147 | } 148 | 149 | func (suite *EthTestSuite) Test_GetBlockTransactionCountByNumber() { 150 | eth := suite.eth 151 | transactionCount, err := eth.GetBlockTransactionCountByNumber("latest") 152 | assert.NoError(suite.T(), err, "Should be no error") 153 | assert.EqualValues(suite.T(), 154 | big.NewInt(0xa), 155 | transactionCount, 156 | "Should be equal") 157 | } 158 | 159 | func (suite *EthTestSuite) Test_GetUncleCountByBlockHash() { 160 | eth := suite.eth 161 | uncleCount, err := eth.GetUncleCountByBlockHash(common.NewHash(common.HexToBytes("0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"))) 162 | assert.NoError(suite.T(), err, "Should be no error") 163 | assert.EqualValues(suite.T(), 164 | big.NewInt(0x1), 165 | uncleCount, 166 | "Should be equal") 167 | } 168 | 169 | func (suite *EthTestSuite) Test_GetUncleCountByBlockNumber() { 170 | eth := suite.eth 171 | uncleCount, err := eth.GetUncleCountByBlockNumber("latest") 172 | assert.NoError(suite.T(), err, "Should be no error") 173 | assert.EqualValues(suite.T(), 174 | big.NewInt(0x1), 175 | uncleCount, 176 | "Should be equal") 177 | } 178 | 179 | func (suite *EthTestSuite) Test_GetCode() { 180 | eth := suite.eth 181 | code, err := eth.GetCode(common.NewAddress(common.HexToBytes("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")), "0x2") 182 | assert.NoError(suite.T(), err, "Should be no error") 183 | assert.EqualValues(suite.T(), 184 | common.HexToBytes("0x600160008035811a818181146012578301005b601b6001356025565b8060005260206000f25b600060078202905091905056"), 185 | code, 186 | "Should be equal") 187 | } 188 | 189 | func (suite *EthTestSuite) Test_Sign() { 190 | eth := suite.eth 191 | signedData, err := eth.Sign(common.NewAddress(common.HexToBytes("0xd1ade25ccd3d550a7eb532ac759cac7be09c2719")), []byte("Schoolbus")) 192 | assert.NoError(suite.T(), err, "Should be no error") 193 | assert.EqualValues(suite.T(), 194 | common.HexToBytes("0x2ac19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601"), 195 | signedData, 196 | "Should be equal") 197 | } 198 | 199 | func (suite *EthTestSuite) Test_SendTransaction() { 200 | eth := suite.eth 201 | req := &common.TransactionRequest{ 202 | From: common.NewAddress(common.HexToBytes("0xb60e8dd61c5d32be8058bb8eb970870f07233155")), 203 | To: common.NewAddress(common.HexToBytes("0xd46e8dd67c5d32be8058bb8eb970870f07244567")), 204 | Gas: big.NewInt(0x76c0), 205 | GasPrice: big.NewInt(0x9184e72a000), 206 | Value: big.NewInt(0x9184e72a), 207 | Data: common.HexToBytes("0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"), 208 | } 209 | tx, err := eth.SendTransaction(req) 210 | assert.NoError(suite.T(), err, "Should be no error") 211 | assert.EqualValues(suite.T(), 212 | common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 213 | tx, 214 | "Should be equal") 215 | } 216 | 217 | func (suite *EthTestSuite) Test_SendRawTransaction() { 218 | eth := suite.eth 219 | tx, err := eth.SendRawTransaction(common.HexToBytes("0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675")) 220 | assert.NoError(suite.T(), err, "Should be no error") 221 | assert.EqualValues(suite.T(), 222 | common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 223 | tx, 224 | "Should be equal") 225 | } 226 | 227 | func (suite *EthTestSuite) Test_Call() { 228 | eth := suite.eth 229 | req := &common.TransactionRequest{ 230 | From: common.NewAddress(common.HexToBytes("0xb60e8dd61c5d32be8058bb8eb970870f07233155")), 231 | To: common.NewAddress(common.HexToBytes("0xd46e8dd67c5d32be8058bb8eb970870f07244567")), 232 | Gas: big.NewInt(0x76c0), 233 | GasPrice: big.NewInt(0x9184e72a000), 234 | Value: big.NewInt(0x9184e72a), 235 | Data: common.HexToBytes("0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"), 236 | } 237 | result, err := eth.Call(req, "latest") 238 | assert.NoError(suite.T(), err, "Should be no error") 239 | assert.EqualValues(suite.T(), 240 | common.HexToBytes("0x"), 241 | result, 242 | "Should be equal") 243 | } 244 | 245 | func (suite *EthTestSuite) Test_EstimateGas() { 246 | eth := suite.eth 247 | req := &common.TransactionRequest{ 248 | From: common.NewAddress(common.HexToBytes("0xb60e8dd61c5d32be8058bb8eb970870f07233155")), 249 | To: common.NewAddress(common.HexToBytes("0xd46e8dd67c5d32be8058bb8eb970870f07244567")), 250 | Gas: big.NewInt(0x76c0), 251 | GasPrice: big.NewInt(0x9184e72a000), 252 | Value: big.NewInt(0x9184e72a), 253 | Data: common.HexToBytes("0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"), 254 | } 255 | gas, err := eth.EstimateGas(req, "latest") 256 | assert.NoError(suite.T(), err, "Should be no error") 257 | assert.EqualValues(suite.T(), 258 | big.NewInt(0x5208), 259 | gas, 260 | "Should be equal") 261 | } 262 | 263 | func (suite *EthTestSuite) Test_GetBlockByHash() { 264 | eth := suite.eth 265 | block := &common.Block{ 266 | Number: big.NewInt(0x1b4), 267 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 268 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 269 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 270 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 271 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 272 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 273 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 274 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 275 | Difficulty: big.NewInt(0x027f07), 276 | TotalDifficulty: big.NewInt(0x027f07), 277 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 278 | Size: big.NewInt(0x027f07), 279 | GasLimit: big.NewInt(0x9f759), 280 | GasUsed: big.NewInt(0x9f759), 281 | Timestamp: big.NewInt(0x54e34e8e), 282 | Transactions: []common.Hash{}, 283 | Uncles: []common.Hash{}, 284 | } 285 | returnedBlock, err := eth.GetBlockByHash(common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), true) 286 | assert.NoError(suite.T(), err, "Should be no error") 287 | assert.EqualValues(suite.T(), 288 | block, returnedBlock, "Should be equal") 289 | } 290 | 291 | func (suite *EthTestSuite) Test_GetBlockByNumber() { 292 | eth := suite.eth 293 | block := &common.Block{ 294 | Number: big.NewInt(0x1b4), 295 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 296 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 297 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 298 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 299 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 300 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 301 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 302 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 303 | Difficulty: big.NewInt(0x027f07), 304 | TotalDifficulty: big.NewInt(0x027f07), 305 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 306 | Size: big.NewInt(0x027f07), 307 | GasLimit: big.NewInt(0x9f759), 308 | GasUsed: big.NewInt(0x9f759), 309 | Timestamp: big.NewInt(0x54e34e8e), 310 | Transactions: []common.Hash{}, 311 | Uncles: []common.Hash{}, 312 | } 313 | returnedBlock, err := eth.GetBlockByNumber("0x1b4", true) 314 | assert.NoError(suite.T(), err, "Should be no error") 315 | assert.EqualValues(suite.T(), 316 | block, returnedBlock, "Should be equal") 317 | } 318 | 319 | func (suite *EthTestSuite) Test_GetTransactionByHash() { 320 | eth := suite.eth 321 | tx := &common.Transaction{ 322 | Hash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 323 | Nonce: common.NewHash(common.HexToBytes("0x")), 324 | BlockHash: common.NewHash(common.HexToBytes("0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b")), 325 | BlockNumber: big.NewInt(0x15df), 326 | TransactionIndex: 0x1, 327 | From: common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 328 | To: common.NewAddress(common.HexToBytes("0x85h43d8a49eeb85d32cf465507dd71d507100c1")), 329 | Value: big.NewInt(0x7f110), 330 | Gas: big.NewInt(0x7f110), 331 | GasPrice: big.NewInt(0x09184e72a000), 332 | Data: common.HexToBytes("0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"), 333 | } 334 | returnedTx, err := eth.GetTransactionByHash(common.NewHash(common.HexToBytes("0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"))) 335 | assert.NoError(suite.T(), err, "Should be no error") 336 | assert.EqualValues(suite.T(), 337 | tx, returnedTx, "Should be equal") 338 | } 339 | 340 | func (suite *EthTestSuite) Test_GetTransactionByHashAndIndex() { 341 | eth := suite.eth 342 | tx := &common.Transaction{ 343 | Hash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 344 | Nonce: common.NewHash(common.HexToBytes("0x")), 345 | BlockHash: common.NewHash(common.HexToBytes("0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b")), 346 | BlockNumber: big.NewInt(0x15df), 347 | TransactionIndex: 0x1, 348 | From: common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 349 | To: common.NewAddress(common.HexToBytes("0x85h43d8a49eeb85d32cf465507dd71d507100c1")), 350 | Value: big.NewInt(0x7f110), 351 | Gas: big.NewInt(0x7f110), 352 | GasPrice: big.NewInt(0x09184e72a000), 353 | Data: common.HexToBytes("0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"), 354 | } 355 | returnedTx, err := eth.GetTransactionByBlockHashAndIndex(common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 0) 356 | assert.NoError(suite.T(), err, "Should be no error") 357 | assert.EqualValues(suite.T(), 358 | tx, returnedTx, "Should be equal") 359 | } 360 | 361 | func (suite *EthTestSuite) Test_GetTransactionByNumberAndIndex() { 362 | eth := suite.eth 363 | tx := &common.Transaction{ 364 | Hash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 365 | Nonce: common.NewHash(common.HexToBytes("0x")), 366 | BlockHash: common.NewHash(common.HexToBytes("0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b")), 367 | BlockNumber: big.NewInt(0x15df), 368 | TransactionIndex: 0x1, 369 | From: common.NewAddress(common.HexToBytes("0x407d73d8a49eeb85d32cf465507dd71d507100c1")), 370 | To: common.NewAddress(common.HexToBytes("0x85h43d8a49eeb85d32cf465507dd71d507100c1")), 371 | Value: big.NewInt(0x7f110), 372 | Gas: big.NewInt(0x7f110), 373 | GasPrice: big.NewInt(0x09184e72a000), 374 | Data: common.HexToBytes("0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"), 375 | } 376 | returnedTx, err := eth.GetTransactionByBlockNumberAndIndex("0x29c", 0) 377 | assert.NoError(suite.T(), err, "Should be no error") 378 | assert.EqualValues(suite.T(), 379 | tx, returnedTx, "Should be equal") 380 | } 381 | 382 | func (suite *EthTestSuite) Test_GetTransactionReceipt() { 383 | eth := suite.eth 384 | receipt := &common.TransactionReceipt{ 385 | Hash: common.NewHash(common.HexToBytes("0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238")), 386 | TransactionIndex: 0x1, 387 | BlockNumber: big.NewInt(0xb), 388 | BlockHash: common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 389 | CumulativeGasUsed: big.NewInt(0x33bc), 390 | GasUsed: big.NewInt(0x4dc), 391 | ContractAddress: common.NewAddress(common.HexToBytes("0xb60e8dd61c5d32be8058bb8eb970870f07233155")), 392 | Logs: []common.Log{}, 393 | } 394 | returnReceipt, err := eth.GetTransactionReceipt(common.NewHash(common.HexToBytes("0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"))) 395 | assert.NoError(suite.T(), err, "Should be no error") 396 | assert.EqualValues(suite.T(), 397 | receipt, returnReceipt, "Should be equal") 398 | } 399 | 400 | func (suite *EthTestSuite) Test_GetUncleByBlockHashAndIndex() { 401 | eth := suite.eth 402 | block := &common.Block{ 403 | Number: big.NewInt(0x1b4), 404 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 405 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 406 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 407 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 408 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 409 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 410 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 411 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 412 | Difficulty: big.NewInt(0x027f07), 413 | TotalDifficulty: big.NewInt(0x027f07), 414 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 415 | Size: big.NewInt(0x027f07), 416 | GasLimit: big.NewInt(0x9f759), 417 | GasUsed: big.NewInt(0x9f759), 418 | Timestamp: big.NewInt(0x54e34e8e), 419 | Transactions: []common.Hash{}, 420 | Uncles: []common.Hash{}, 421 | } 422 | returnedBlock, err := eth.GetUncleByBlockHashAndIndex(common.NewHash(common.HexToBytes("0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b")), 0) 423 | assert.NoError(suite.T(), err, "Should be no error") 424 | assert.EqualValues(suite.T(), 425 | block, returnedBlock, "Should be equal") 426 | } 427 | 428 | func (suite *EthTestSuite) Test_GetUncleByBlockNumberAndIndex() { 429 | eth := suite.eth 430 | block := &common.Block{ 431 | Number: big.NewInt(0x1b4), 432 | Hash: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 433 | ParentHash: common.NewHash(common.HexToBytes("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5")), 434 | Nonce: common.NewHash(common.HexToBytes("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")), 435 | Sha3Uncles: common.NewHash(common.HexToBytes("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), 436 | Bloom: common.NewHash(common.HexToBytes("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")), 437 | TransactionRoot: common.NewHash(common.HexToBytes("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), 438 | StateRoot: common.NewHash(common.HexToBytes("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff")), 439 | Miner: common.NewAddress(common.HexToBytes("0x4e65fda2159562a496f9f3522f89122a3088497a")), 440 | Difficulty: big.NewInt(0x027f07), 441 | TotalDifficulty: big.NewInt(0x027f07), 442 | ExtraData: common.NewHash(common.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")), 443 | Size: big.NewInt(0x027f07), 444 | GasLimit: big.NewInt(0x9f759), 445 | GasUsed: big.NewInt(0x9f759), 446 | Timestamp: big.NewInt(0x54e34e8e), 447 | Transactions: []common.Hash{}, 448 | Uncles: []common.Hash{}, 449 | } 450 | returnedBlock, err := eth.GetUncleByBlockNumberAndIndex("0x29c", 0) 451 | assert.NoError(suite.T(), err, "Should be no error") 452 | assert.EqualValues(suite.T(), 453 | block, returnedBlock, "Should be equal") 454 | } 455 | 456 | func (suite *EthTestSuite) Test_GetCompilers() { 457 | eth := suite.eth 458 | compilers := []string{"solidity", "lll", "serpent"} 459 | returnedCompilers, err := eth.GetCompilers() 460 | assert.NoError(suite.T(), err, "Should be no error") 461 | assert.EqualValues(suite.T(), 462 | compilers, returnedCompilers, "Should be equal") 463 | } 464 | 465 | func (suite *EthTestSuite) Test_NewFilter() { 466 | eth := suite.eth 467 | option := &FilterOption{} 468 | filter, err := eth.NewFilter(option) 469 | assert.NoError(suite.T(), err, "Should be no error") 470 | if assert.NotNil(suite.T(), filter, "Should be equal") { 471 | assert.EqualValues(suite.T(), 472 | 1, filter.ID(), "Should be equal") 473 | } 474 | } 475 | 476 | func (suite *EthTestSuite) Test_NewBlockFilter() { 477 | eth := suite.eth 478 | filter, err := eth.NewBlockFilter() 479 | assert.NoError(suite.T(), err, "Should be no error") 480 | if assert.NotNil(suite.T(), filter, "Should be equal") { 481 | assert.EqualValues(suite.T(), 482 | 1, filter.ID(), "Should be equal") 483 | } 484 | } 485 | 486 | func (suite *EthTestSuite) Test_NewPendingTransactionFilter() { 487 | eth := suite.eth 488 | filter, err := eth.NewPendingTransactionFilter() 489 | assert.NoError(suite.T(), err, "Should be no error") 490 | if assert.NotNil(suite.T(), filter, "Should be equal") { 491 | assert.EqualValues(suite.T(), 492 | 1, filter.ID(), "Should be equal") 493 | } 494 | } 495 | 496 | func (suite *EthTestSuite) Test_UninstallFilter() { 497 | eth := suite.eth 498 | option := &FilterOption{} 499 | filter, err := eth.NewFilter(option) 500 | ok, err := eth.UninstallFilter(filter) 501 | assert.NoError(suite.T(), err, "Should be no error") 502 | assert.True(suite.T(), ok, "Should be true") 503 | } 504 | 505 | func (suite *EthTestSuite) Test_GetFilterChanges() { 506 | eth := suite.eth 507 | option := &FilterOption{} 508 | filter, err := eth.NewFilter(option) 509 | logs := []common.Log{ 510 | { 511 | LogIndex: 0x1, 512 | BlockNumber: big.NewInt(0x1b4), 513 | BlockHash: common.NewHash(common.HexToBytes("0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 514 | TransactionHash: common.NewHash(common.HexToBytes("0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf")), 515 | TransactionIndex: 0, 516 | Address: common.NewAddress(common.HexToBytes("0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 517 | Data: []byte("0000000000000000000000000000000000000000000000000000000000000000"), 518 | Topics: common.Topics{ 519 | { 520 | Data: common.HexToBytes("0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5"), 521 | }, 522 | }, 523 | }, 524 | } 525 | returnedLogs, err := eth.GetFilterChanges(filter) 526 | if assert.NoError(suite.T(), err, "Should be no error") { 527 | for i, l := range returnedLogs { 528 | log := common.Log{} 529 | rawBytes, err := json.Marshal(l) 530 | assert.NoError(suite.T(), err, "Should be no error") 531 | err = json.Unmarshal(rawBytes, &log) 532 | assert.NoError(suite.T(), err, "Should be no error") 533 | assert.EqualValues(suite.T(), logs[i], log, "Should be equal") 534 | } 535 | } 536 | } 537 | 538 | func (suite *EthTestSuite) Test_GetFilterLogs() { 539 | eth := suite.eth 540 | option := &FilterOption{} 541 | filter, err := eth.NewFilter(option) 542 | logs := []common.Log{ 543 | { 544 | LogIndex: 0x1, 545 | BlockNumber: big.NewInt(0x1b4), 546 | BlockHash: common.NewHash(common.HexToBytes("0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 547 | TransactionHash: common.NewHash(common.HexToBytes("0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf")), 548 | TransactionIndex: 0, 549 | Address: common.NewAddress(common.HexToBytes("0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 550 | Data: []byte("0000000000000000000000000000000000000000000000000000000000000000"), 551 | Topics: common.Topics{ 552 | { 553 | Data: common.HexToBytes("0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5"), 554 | }, 555 | }, 556 | }, 557 | } 558 | returnedLogs, err := eth.GetFilterLogs(filter) 559 | if assert.NoError(suite.T(), err, "Should be no error") { 560 | for i, l := range returnedLogs { 561 | log := common.Log{} 562 | rawBytes, err := json.Marshal(l) 563 | assert.NoError(suite.T(), err, "Should be no error") 564 | err = json.Unmarshal(rawBytes, &log) 565 | assert.NoError(suite.T(), err, "Should be no error") 566 | assert.EqualValues(suite.T(), logs[i], log, "Should be equal") 567 | } 568 | } 569 | } 570 | 571 | func (suite *EthTestSuite) Test_GetLogs() { 572 | eth := suite.eth 573 | option := &FilterOption{} 574 | filter, err := eth.NewFilter(option) 575 | logs := []common.Log{ 576 | { 577 | LogIndex: 0x1, 578 | BlockNumber: big.NewInt(0x1b4), 579 | BlockHash: common.NewHash(common.HexToBytes("0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 580 | TransactionHash: common.NewHash(common.HexToBytes("0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf")), 581 | TransactionIndex: 0, 582 | Address: common.NewAddress(common.HexToBytes("0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d")), 583 | Data: []byte("0000000000000000000000000000000000000000000000000000000000000000"), 584 | Topics: common.Topics{ 585 | { 586 | Data: common.HexToBytes("0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5"), 587 | }, 588 | }, 589 | }, 590 | } 591 | returnedLogs, err := eth.GetLogs(filter) 592 | if assert.NoError(suite.T(), err, "Should be no error") { 593 | for i, l := range returnedLogs { 594 | log := common.Log{} 595 | rawBytes, err := json.Marshal(l) 596 | assert.NoError(suite.T(), err, "Should be no error") 597 | err = json.Unmarshal(rawBytes, &log) 598 | assert.NoError(suite.T(), err, "Should be no error") 599 | assert.EqualValues(suite.T(), logs[i], log, "Should be equal") 600 | } 601 | } 602 | } 603 | 604 | func (suite *EthTestSuite) Test_GetWork() { 605 | eth := suite.eth 606 | works := []string{ 607 | "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", 608 | "0x5EED00000000000000000000000000005EED0000000000000000000000000000", 609 | "0xd1ff1c01710000000000000000000000d1ff1c01710000000000000000000000"} 610 | header, seed, boundary, err := eth.GetWork() 611 | assert.NoError(suite.T(), err, "Should be no error") 612 | assert.EqualValues(suite.T(), strings.ToLower(works[0]), header.String(), "Should be equal") 613 | assert.EqualValues(suite.T(), strings.ToLower(works[1]), seed.String(), "Should be equal") 614 | assert.EqualValues(suite.T(), strings.ToLower(works[2]), boundary.String(), "Should be equal") 615 | } 616 | 617 | func (suite *EthTestSuite) Test_SubmitWork() { 618 | eth := suite.eth 619 | header := common.NewHash(common.HexToBytes("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")) 620 | mixDigest := common.NewHash(common.HexToBytes("0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000")) 621 | result, err := eth.SubmitWork(0, header, mixDigest) 622 | assert.NoError(suite.T(), err, "Should be no error") 623 | assert.True(suite.T(), result, "Should be true") 624 | } 625 | 626 | func (suite *EthTestSuite) SetupTest() { 627 | suite.web3 = NewWeb3(test.NewMockHTTPProvider()) 628 | suite.eth = suite.web3.Eth 629 | } 630 | 631 | func Test_EthTestSuite(t *testing.T) { 632 | suite.Run(t, new(EthTestSuite)) 633 | } 634 | -------------------------------------------------------------------------------- /web3/eth.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alan Chen 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 10 | // 2. Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation 12 | // and/or other materials provided with the distribution. 13 | // 14 | // 3. Neither the name of the copyright holder nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package web3 31 | 32 | import ( 33 | "encoding/json" 34 | "fmt" 35 | "math/big" 36 | "strconv" 37 | 38 | "github.com/alanchchen/web3go/common" 39 | "github.com/alanchchen/web3go/rpc" 40 | ) 41 | 42 | // Eth ... 43 | type Eth interface { 44 | ProtocolVersion() (string, error) 45 | Syncing() (common.SyncStatus, error) 46 | Coinbase() (common.Address, error) 47 | Mining() (bool, error) 48 | HashRate() (uint64, error) 49 | GasPrice() (*big.Int, error) 50 | Accounts() ([]common.Address, error) 51 | BlockNumber() (*big.Int, error) 52 | GetBalance(address common.Address, quantity string) (*big.Int, error) 53 | GetStorageAt(address common.Address, position uint64, quantity string) (uint64, error) 54 | GetTransactionCount(address common.Address, quantity string) (*big.Int, error) 55 | GetBlockTransactionCountByHash(hash common.Hash) (*big.Int, error) 56 | GetBlockTransactionCountByNumber(quantity string) (*big.Int, error) 57 | GetUncleCountByBlockHash(hash common.Hash) (*big.Int, error) 58 | GetUncleCountByBlockNumber(quantity string) (*big.Int, error) 59 | GetCode(address common.Address, quantity string) ([]byte, error) 60 | Sign(address common.Address, data []byte) ([]byte, error) 61 | SendTransaction(tx *common.TransactionRequest) (common.Hash, error) 62 | SendRawTransaction(tx []byte) (common.Hash, error) 63 | Call(tx *common.TransactionRequest, quantity string) ([]byte, error) 64 | EstimateGas(tx *common.TransactionRequest, quantity string) (*big.Int, error) 65 | GetBlockByHash(hash common.Hash, full bool) (*common.Block, error) 66 | GetBlockByNumber(quantity string, full bool) (*common.Block, error) 67 | GetTransactionByHash(hash common.Hash) (*common.Transaction, error) 68 | GetTransactionByBlockHashAndIndex(hash common.Hash, index uint64) (*common.Transaction, error) 69 | GetTransactionByBlockNumberAndIndex(quantity string, index uint64) (*common.Transaction, error) 70 | GetTransactionReceipt(hash common.Hash) (*common.TransactionReceipt, error) 71 | GetUncleByBlockHashAndIndex(hash common.Hash, index uint64) (*common.Block, error) 72 | GetUncleByBlockNumberAndIndex(quantity string, index uint64) (*common.Block, error) 73 | GetCompilers() ([]string, error) 74 | // GompileLLL 75 | // CompileSolidity 76 | // CompileSerpent 77 | NewFilter(option *FilterOption) (Filter, error) 78 | NewBlockFilter() (Filter, error) 79 | NewPendingTransactionFilter() (Filter, error) 80 | UninstallFilter(filter Filter) (bool, error) 81 | GetFilterChanges(filter Filter) ([]interface{}, error) 82 | GetFilterLogs(filter Filter) ([]interface{}, error) 83 | GetLogs(filter Filter) ([]interface{}, error) 84 | GetWork() (common.Hash, common.Hash, common.Hash, error) 85 | SubmitWork(nonce uint64, header common.Hash, mixDigest common.Hash) (bool, error) 86 | // SubmitHashrate 87 | } 88 | 89 | // EthAPI ... 90 | type EthAPI struct { 91 | rpc rpc.RPC 92 | requestManager *requestManager 93 | } 94 | 95 | // NewEthAPI ... 96 | func newEthAPI(requestManager *requestManager) Eth { 97 | return &EthAPI{requestManager: requestManager} 98 | } 99 | 100 | // ProtocolVersion returns the current ethereum protocol version. 101 | func (eth *EthAPI) ProtocolVersion() (string, error) { 102 | req := eth.requestManager.newRequest("eth_protocolVersion") 103 | resp, err := eth.requestManager.send(req) 104 | if err != nil { 105 | return "", err 106 | } 107 | 108 | if resp.Error() != nil { 109 | return "", resp.Error() 110 | } 111 | 112 | return resp.Get("result").(string), nil 113 | } 114 | 115 | // Syncing returns true with an object with data about the sync status or false 116 | // with nil. 117 | func (eth *EthAPI) Syncing() (common.SyncStatus, error) { 118 | req := eth.requestManager.newRequest("eth_syncing") 119 | resp, err := eth.requestManager.send(req) 120 | if err != nil { 121 | return common.SyncStatus{ 122 | Result: false, 123 | }, err 124 | } 125 | 126 | if resp.Error() != nil { 127 | return common.SyncStatus{ 128 | Result: false, 129 | }, resp.Error() 130 | } 131 | 132 | result := resp.Get("result") 133 | switch result.(type) { 134 | case bool: 135 | return common.SyncStatus{ 136 | Result: false, 137 | }, nil 138 | default: 139 | var err error 140 | var resultBlob []byte 141 | r := common.SyncStatus{ 142 | Result: true, 143 | } 144 | if resultBlob, err = json.Marshal(result); err == nil { 145 | if err = json.Unmarshal(resultBlob, &r); err == nil { 146 | return r, nil 147 | } 148 | } 149 | 150 | return common.SyncStatus{ 151 | Result: false, 152 | }, err 153 | } 154 | } 155 | 156 | // Coinbase returns the client coinbase address. 157 | func (eth *EthAPI) Coinbase() (addr common.Address, err error) { 158 | req := eth.requestManager.newRequest("eth_coinbase") 159 | resp, err := eth.requestManager.send(req) 160 | if err != nil { 161 | return common.NewAddress(nil), err 162 | } 163 | 164 | if resp.Error() != nil { 165 | return common.NewAddress(nil), resp.Error() 166 | } 167 | 168 | return common.StringToAddress(resp.Get("result").(string)), nil 169 | } 170 | 171 | // Mining returns true if client is actively mining new blocks. 172 | func (eth *EthAPI) Mining() (bool, error) { 173 | req := eth.requestManager.newRequest("eth_mining") 174 | resp, err := eth.requestManager.send(req) 175 | if err != nil { 176 | return false, err 177 | } 178 | 179 | if resp.Error() != nil { 180 | return false, resp.Error() 181 | } 182 | 183 | return resp.Get("result").(bool), nil 184 | } 185 | 186 | // HashRate returns the number of hashes per second that the node is mining 187 | // with. 188 | func (eth *EthAPI) HashRate() (uint64, error) { 189 | req := eth.requestManager.newRequest("eth_hashrate") 190 | resp, err := eth.requestManager.send(req) 191 | if err != nil { 192 | return 0, err 193 | } 194 | 195 | if resp.Error() != nil { 196 | return 0, resp.Error() 197 | } 198 | 199 | result, err := strconv.ParseUint(common.HexToString(resp.Get("result").(string)), 16, 64) 200 | if err != nil { 201 | return 0, err 202 | } 203 | return result, nil 204 | } 205 | 206 | // GasPrice returns the current price per gas in wei. 207 | func (eth *EthAPI) GasPrice() (result *big.Int, err error) { 208 | req := eth.requestManager.newRequest("eth_gasPrice") 209 | resp, err := eth.requestManager.send(req) 210 | if err != nil { 211 | return nil, err 212 | } 213 | 214 | if resp.Error() != nil { 215 | return nil, resp.Error() 216 | } 217 | 218 | result = new(big.Int) 219 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 220 | if !ok { 221 | return nil, fmt.Errorf("%v", resp.Get("result")) 222 | } 223 | return result, nil 224 | } 225 | 226 | // Accounts returns a list of addresses owned by client. 227 | func (eth *EthAPI) Accounts() (addrs []common.Address, err error) { 228 | req := eth.requestManager.newRequest("eth_accounts") 229 | resp, err := eth.requestManager.send(req) 230 | if err != nil { 231 | return nil, err 232 | } 233 | 234 | if resp.Error() != nil { 235 | return nil, resp.Error() 236 | } 237 | 238 | results := resp.Get("result").([]interface{}) 239 | for _, r := range results { 240 | addrs = append(addrs, common.StringToAddress(r.(string))) 241 | } 242 | return addrs, nil 243 | } 244 | 245 | // BlockNumber returns the number of most recent block. 246 | func (eth *EthAPI) BlockNumber() (result *big.Int, err error) { 247 | req := eth.requestManager.newRequest("eth_blockNumber") 248 | resp, err := eth.requestManager.send(req) 249 | if err != nil { 250 | return nil, err 251 | } 252 | 253 | if resp.Error() != nil { 254 | return nil, resp.Error() 255 | } 256 | 257 | result = new(big.Int) 258 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 259 | if !ok { 260 | return nil, fmt.Errorf("%v", resp.Get("result")) 261 | } 262 | return result, nil 263 | } 264 | 265 | // GetBalance returns the balance of the account of given address. 266 | func (eth *EthAPI) GetBalance(address common.Address, quantity string) (result *big.Int, err error) { 267 | req := eth.requestManager.newRequest("eth_getBalance") 268 | req.Set("params", []string{address.String(), quantity}) 269 | resp, err := eth.requestManager.send(req) 270 | if err != nil { 271 | return nil, err 272 | } 273 | 274 | if resp.Error() != nil { 275 | return nil, resp.Error() 276 | } 277 | 278 | result = new(big.Int) 279 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 280 | if !ok { 281 | return nil, fmt.Errorf("%v", resp.Get("result")) 282 | } 283 | return result, nil 284 | } 285 | 286 | // GetStorageAt returns the value from a storage position at a given address. 287 | func (eth *EthAPI) GetStorageAt(address common.Address, position uint64, quantity string) (uint64, error) { 288 | req := eth.requestManager.newRequest("eth_getStorageAt") 289 | req.Set("params", []string{address.String(), fmt.Sprintf("%v", position), quantity}) 290 | resp, err := eth.requestManager.send(req) 291 | if err != nil { 292 | return 0, err 293 | } 294 | 295 | if resp.Error() != nil { 296 | return 0, resp.Error() 297 | } 298 | 299 | result, err := strconv.ParseUint(common.HexToString(resp.Get("result").(string)), 16, 64) 300 | if err != nil { 301 | return 0, err 302 | } 303 | return result, nil 304 | } 305 | 306 | // GetTransactionCount returns the number of transactions sent from an address. 307 | func (eth *EthAPI) GetTransactionCount(address common.Address, quantity string) (result *big.Int, err error) { 308 | req := eth.requestManager.newRequest("eth_getTransactionCount") 309 | req.Set("params", []string{address.String(), quantity}) 310 | resp, err := eth.requestManager.send(req) 311 | if err != nil { 312 | return nil, err 313 | } 314 | 315 | if resp.Error() != nil { 316 | return nil, resp.Error() 317 | } 318 | 319 | result = new(big.Int) 320 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 321 | if !ok { 322 | return nil, fmt.Errorf("%v", resp.Get("result")) 323 | } 324 | return result, nil 325 | } 326 | 327 | // GetBlockTransactionCountByHash returns the number of transactions in a block 328 | // from a block matching the given block hash. 329 | func (eth *EthAPI) GetBlockTransactionCountByHash(hash common.Hash) (result *big.Int, err error) { 330 | req := eth.requestManager.newRequest("eth_getBlockTransactionCountByHash") 331 | req.Set("params", hash.String()) 332 | resp, err := eth.requestManager.send(req) 333 | if err != nil { 334 | return nil, err 335 | } 336 | 337 | if resp.Error() != nil { 338 | return nil, resp.Error() 339 | } 340 | 341 | result = new(big.Int) 342 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 343 | if !ok { 344 | return nil, fmt.Errorf("%v", resp.Get("result")) 345 | } 346 | return result, nil 347 | } 348 | 349 | // GetBlockTransactionCountByNumber returns the number of transactions in a 350 | // block from a block matching the given block number. 351 | func (eth *EthAPI) GetBlockTransactionCountByNumber(quantity string) (result *big.Int, err error) { 352 | req := eth.requestManager.newRequest("eth_getBlockTransactionCountByNumber") 353 | req.Set("params", quantity) 354 | resp, err := eth.requestManager.send(req) 355 | if err != nil { 356 | return nil, err 357 | } 358 | 359 | if resp.Error() != nil { 360 | return nil, resp.Error() 361 | } 362 | 363 | result = new(big.Int) 364 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 365 | if !ok { 366 | return nil, fmt.Errorf("%v", resp.Get("result")) 367 | } 368 | return result, nil 369 | } 370 | 371 | // GetUncleCountByBlockHash returns the number of uncles in a block from a block 372 | // matching the given block hash. 373 | func (eth *EthAPI) GetUncleCountByBlockHash(hash common.Hash) (result *big.Int, err error) { 374 | req := eth.requestManager.newRequest("eth_getUncleCountByBlockHash") 375 | req.Set("params", hash.String()) 376 | resp, err := eth.requestManager.send(req) 377 | if err != nil { 378 | return nil, err 379 | } 380 | 381 | if resp.Error() != nil { 382 | return nil, resp.Error() 383 | } 384 | 385 | result = new(big.Int) 386 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 387 | if !ok { 388 | return nil, fmt.Errorf("%v", resp.Get("result")) 389 | } 390 | return result, nil 391 | } 392 | 393 | // GetUncleCountByBlockNumber returns the number of uncles in a block from a 394 | // block matching the given block number. 395 | func (eth *EthAPI) GetUncleCountByBlockNumber(quantity string) (result *big.Int, err error) { 396 | req := eth.requestManager.newRequest("eth_getUncleCountByBlockNumber") 397 | req.Set("params", quantity) 398 | resp, err := eth.requestManager.send(req) 399 | if err != nil { 400 | return nil, err 401 | } 402 | 403 | if resp.Error() != nil { 404 | return nil, resp.Error() 405 | } 406 | 407 | result = new(big.Int) 408 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 409 | if !ok { 410 | return nil, fmt.Errorf("%v", resp.Get("result")) 411 | } 412 | return result, nil 413 | } 414 | 415 | // GetCode returns code at a given address. 416 | func (eth *EthAPI) GetCode(address common.Address, quantity string) ([]byte, error) { 417 | req := eth.requestManager.newRequest("eth_getCode") 418 | req.Set("params", []string{address.String(), quantity}) 419 | resp, err := eth.requestManager.send(req) 420 | if err != nil { 421 | return nil, err 422 | } 423 | 424 | if resp.Error() != nil { 425 | return nil, resp.Error() 426 | } 427 | 428 | return common.HexToBytes(resp.Get("result").(string)), nil 429 | } 430 | 431 | // Sign signs data with a given address. 432 | func (eth *EthAPI) Sign(address common.Address, data []byte) ([]byte, error) { 433 | req := eth.requestManager.newRequest("eth_sign") 434 | req.Set("params", []string{address.String(), string(data)}) 435 | resp, err := eth.requestManager.send(req) 436 | if err != nil { 437 | return nil, err 438 | } 439 | 440 | if resp.Error() != nil { 441 | return nil, resp.Error() 442 | } 443 | 444 | return common.HexToBytes(resp.Get("result").(string)), nil 445 | } 446 | 447 | // SendTransaction creates new message call transaction or a contract creation, 448 | // if the data field contains code. 449 | func (eth *EthAPI) SendTransaction(tx *common.TransactionRequest) (hash common.Hash, err error) { 450 | req := eth.requestManager.newRequest("eth_sendTransaction") 451 | req.Set("params", []string{tx.String()}) 452 | resp, err := eth.requestManager.send(req) 453 | if err != nil { 454 | return common.NewHash(nil), err 455 | } 456 | 457 | if resp.Error() != nil { 458 | return common.NewHash(nil), resp.Error() 459 | } 460 | 461 | return common.StringToHash(resp.Get("result").(string)), nil 462 | } 463 | 464 | // SendRawTransaction creates new message call transaction or a contract 465 | // creation for signed transactions. 466 | func (eth *EthAPI) SendRawTransaction(tx []byte) (hash common.Hash, err error) { 467 | req := eth.requestManager.newRequest("eth_sendRawTransaction") 468 | req.Set("params", []string{common.BytesToHex(tx)}) 469 | resp, err := eth.requestManager.send(req) 470 | if err != nil { 471 | return common.NewHash(nil), err 472 | } 473 | 474 | if resp.Error() != nil { 475 | return common.NewHash(nil), resp.Error() 476 | } 477 | 478 | return common.StringToHash(resp.Get("result").(string)), nil 479 | } 480 | 481 | // Call executes a new message call immediately without creating a transaction 482 | // on the block chain. 483 | func (eth *EthAPI) Call(tx *common.TransactionRequest, quantity string) ([]byte, error) { 484 | req := eth.requestManager.newRequest("eth_call") 485 | req.Set("params", []string{tx.String(), quantity}) 486 | resp, err := eth.requestManager.send(req) 487 | if err != nil { 488 | return nil, err 489 | } 490 | 491 | if resp.Error() != nil { 492 | return nil, resp.Error() 493 | } 494 | 495 | return common.HexToBytes(resp.Get("result").(string)), nil 496 | } 497 | 498 | // EstimateGas makes a call or transaction, which won't be added to the 499 | // blockchain and returns the used gas, which can be used for estimating the 500 | // used gas. 501 | func (eth *EthAPI) EstimateGas(tx *common.TransactionRequest, quantity string) (result *big.Int, err error) { 502 | req := eth.requestManager.newRequest("eth_estimateGas") 503 | req.Set("params", []string{tx.String(), quantity}) 504 | resp, err := eth.requestManager.send(req) 505 | if err != nil { 506 | return nil, err 507 | } 508 | 509 | if resp.Error() != nil { 510 | return nil, resp.Error() 511 | } 512 | 513 | result = new(big.Int) 514 | _, ok := result.SetString(common.HexToString(resp.Get("result").(string)), 16) 515 | if !ok { 516 | return nil, fmt.Errorf("%v", resp.Get("result")) 517 | } 518 | return result, nil 519 | } 520 | 521 | // GetBlockByHash returns information about a block by hash. 522 | func (eth *EthAPI) GetBlockByHash(hash common.Hash, full bool) (*common.Block, error) { 523 | req := eth.requestManager.newRequest("eth_getBlockByHash") 524 | req.Set("params", []interface{}{hash.String(), full}) 525 | resp, err := eth.requestManager.send(req) 526 | if err != nil { 527 | return nil, err 528 | } 529 | 530 | if resp.Error() != nil { 531 | return nil, resp.Error() 532 | } 533 | 534 | result := &jsonBlock{} 535 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 536 | if err := json.Unmarshal(jsonBytes, result); err == nil { 537 | return result.ToBlock(), nil 538 | } 539 | } 540 | 541 | return nil, fmt.Errorf("%v", resp.Get("result")) 542 | } 543 | 544 | // GetBlockByNumber returns information about a block by block number. 545 | func (eth *EthAPI) GetBlockByNumber(quantity string, full bool) (*common.Block, error) { 546 | req := eth.requestManager.newRequest("eth_getBlockByNumber") 547 | req.Set("params", []interface{}{quantity, full}) 548 | resp, err := eth.requestManager.send(req) 549 | if err != nil { 550 | return nil, err 551 | } 552 | 553 | if resp.Error() != nil { 554 | return nil, resp.Error() 555 | } 556 | 557 | result := &jsonBlock{} 558 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 559 | if err := json.Unmarshal(jsonBytes, result); err == nil { 560 | return result.ToBlock(), nil 561 | } 562 | } 563 | 564 | return nil, fmt.Errorf("%v", resp.Get("result")) 565 | } 566 | 567 | // GetTransactionByHash returns the information about a transaction requested by 568 | // transaction hash. 569 | func (eth *EthAPI) GetTransactionByHash(hash common.Hash) (*common.Transaction, error) { 570 | req := eth.requestManager.newRequest("eth_getTransactionByHash") 571 | req.Set("params", hash.String()) 572 | resp, err := eth.requestManager.send(req) 573 | if err != nil { 574 | return nil, err 575 | } 576 | 577 | if resp.Error() != nil { 578 | return nil, resp.Error() 579 | } 580 | 581 | result := &jsonTransaction{} 582 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 583 | if err := json.Unmarshal(jsonBytes, result); err == nil { 584 | return result.ToTransaction(), nil 585 | } 586 | } 587 | 588 | return nil, fmt.Errorf("%v", resp.Get("result")) 589 | } 590 | 591 | // GetTransactionByBlockHashAndIndex returns information about a transaction by 592 | // block hash and transaction index position. 593 | func (eth *EthAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, index uint64) (*common.Transaction, error) { 594 | req := eth.requestManager.newRequest("eth_getTransactionByBlockHashAndIndex") 595 | req.Set("params", []string{hash.String(), fmt.Sprintf("%v", index)}) 596 | resp, err := eth.requestManager.send(req) 597 | if err != nil { 598 | return nil, err 599 | } 600 | 601 | if resp.Error() != nil { 602 | return nil, resp.Error() 603 | } 604 | 605 | result := &jsonTransaction{} 606 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 607 | if err := json.Unmarshal(jsonBytes, result); err == nil { 608 | return result.ToTransaction(), nil 609 | } 610 | } 611 | 612 | return nil, fmt.Errorf("%v", resp.Get("result")) 613 | } 614 | 615 | // GetTransactionByBlockNumberAndIndex returns information about a transaction 616 | // by block number and transaction index position. 617 | func (eth *EthAPI) GetTransactionByBlockNumberAndIndex(quantity string, index uint64) (*common.Transaction, error) { 618 | req := eth.requestManager.newRequest("eth_getTransactionByBlockNumberAndIndex") 619 | req.Set("params", []string{quantity, fmt.Sprintf("%v", index)}) 620 | resp, err := eth.requestManager.send(req) 621 | if err != nil { 622 | return nil, err 623 | } 624 | 625 | if resp.Error() != nil { 626 | return nil, resp.Error() 627 | } 628 | 629 | result := &jsonTransaction{} 630 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 631 | if err := json.Unmarshal(jsonBytes, result); err == nil { 632 | return result.ToTransaction(), nil 633 | } 634 | } 635 | 636 | return nil, fmt.Errorf("%v", resp.Get("result")) 637 | } 638 | 639 | // GetTransactionReceipt Returns the receipt of a transaction by transaction hash. 640 | func (eth *EthAPI) GetTransactionReceipt(hash common.Hash) (*common.TransactionReceipt, error) { 641 | req := eth.requestManager.newRequest("eth_getTransactionReceipt") 642 | req.Set("params", hash.String()) 643 | resp, err := eth.requestManager.send(req) 644 | if err != nil { 645 | return nil, err 646 | } 647 | 648 | if resp.Error() != nil { 649 | return nil, resp.Error() 650 | } 651 | 652 | result := &jsonTransactionReceipt{} 653 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 654 | if err := json.Unmarshal(jsonBytes, result); err == nil { 655 | return result.ToTransactionReceipt(), nil 656 | } 657 | } 658 | 659 | return nil, fmt.Errorf("%v", resp.Get("result")) 660 | } 661 | 662 | // GetUncleByBlockHashAndIndex returns information about a uncle of a block by 663 | // hash and uncle index position. 664 | func (eth *EthAPI) GetUncleByBlockHashAndIndex(hash common.Hash, index uint64) (*common.Block, error) { 665 | req := eth.requestManager.newRequest("eth_getUncleByBlockHashAndIndex") 666 | req.Set("params", []string{hash.String(), fmt.Sprintf("%d", index)}) 667 | resp, err := eth.requestManager.send(req) 668 | if err != nil { 669 | return nil, err 670 | } 671 | 672 | if resp.Error() != nil { 673 | return nil, resp.Error() 674 | } 675 | 676 | result := &jsonBlock{} 677 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 678 | if err := json.Unmarshal(jsonBytes, result); err == nil { 679 | return result.ToBlock(), nil 680 | } 681 | } 682 | 683 | return nil, fmt.Errorf("%v", resp.Get("result")) 684 | } 685 | 686 | // GetUncleByBlockNumberAndIndex returns information about a uncle of a block by 687 | // number and uncle index position. 688 | func (eth *EthAPI) GetUncleByBlockNumberAndIndex(quantity string, index uint64) (*common.Block, error) { 689 | req := eth.requestManager.newRequest("eth_getUncleByBlockNumberAndIndex") 690 | req.Set("params", []string{quantity, fmt.Sprintf("%d", index)}) 691 | resp, err := eth.requestManager.send(req) 692 | if err != nil { 693 | return nil, err 694 | } 695 | 696 | if resp.Error() != nil { 697 | return nil, resp.Error() 698 | } 699 | 700 | result := &jsonBlock{} 701 | if jsonBytes, err := json.Marshal(resp.Get("result")); err == nil { 702 | if err := json.Unmarshal(jsonBytes, result); err == nil { 703 | return result.ToBlock(), nil 704 | } 705 | } 706 | 707 | return nil, fmt.Errorf("%v", resp.Get("result")) 708 | } 709 | 710 | // GetCompilers returns a list of available compilers in the client. 711 | func (eth *EthAPI) GetCompilers() (result []string, err error) { 712 | req := eth.requestManager.newRequest("eth_getCompilers") 713 | resp, err := eth.requestManager.send(req) 714 | if err != nil { 715 | return nil, err 716 | } 717 | 718 | if resp.Error() != nil { 719 | return nil, resp.Error() 720 | } 721 | 722 | for _, r := range resp.Get("result").([]interface{}) { 723 | result = append(result, r.(string)) 724 | } 725 | return result, nil 726 | } 727 | 728 | // NewFilter creates a filter object, based on filter options, to notify when 729 | // the state changes (logs). To check if the state has changed, call 730 | // eth_getFilterChanges. 731 | func (eth *EthAPI) NewFilter(option *FilterOption) (Filter, error) { 732 | req := eth.requestManager.newRequest("eth_newFilter") 733 | if option == nil { 734 | option = &FilterOption{} 735 | } 736 | req.Set("params", option) 737 | resp, err := eth.requestManager.send(req) 738 | if err != nil { 739 | return nil, err 740 | } 741 | 742 | if resp.Error() != nil { 743 | return nil, resp.Error() 744 | } 745 | 746 | id, err := strconv.ParseUint(common.HexToString(resp.Get("result").(string)), 16, 64) 747 | if err != nil { 748 | return nil, err 749 | } 750 | return newFilter(eth, TypeNormal, id), nil 751 | } 752 | 753 | // NewBlockFilter creates a filter in the node, to notify when a new block 754 | // arrives. To check if the state has changed, call eth_getFilterChanges. 755 | func (eth *EthAPI) NewBlockFilter() (Filter, error) { 756 | req := eth.requestManager.newRequest("eth_newBlockFilter") 757 | resp, err := eth.requestManager.send(req) 758 | if err != nil { 759 | return nil, err 760 | } 761 | 762 | if resp.Error() != nil { 763 | return nil, resp.Error() 764 | } 765 | 766 | id, err := strconv.ParseUint(common.HexToString(resp.Get("result").(string)), 16, 64) 767 | if err != nil { 768 | return nil, err 769 | } 770 | return newFilter(eth, TypeBlockFilter, id), nil 771 | } 772 | 773 | // NewPendingTransactionFilter creates a filter in the node, to notify when new 774 | // pending transactions arrive. To check if the state has changed, call 775 | // eth_getFilterChanges. 776 | func (eth *EthAPI) NewPendingTransactionFilter() (Filter, error) { 777 | req := eth.requestManager.newRequest("eth_newPendingTransactionFilter") 778 | resp, err := eth.requestManager.send(req) 779 | if err != nil { 780 | return nil, err 781 | } 782 | 783 | if resp.Error() != nil { 784 | return nil, resp.Error() 785 | } 786 | 787 | id, err := strconv.ParseUint(common.HexToString(resp.Get("result").(string)), 16, 64) 788 | if err != nil { 789 | return nil, err 790 | } 791 | return newFilter(eth, TypeTransactionFilter, id), nil 792 | } 793 | 794 | // UninstallFilter uninstalls a filter with given id. Should always be called 795 | // when watch is no longer needed. Additonally Filters timeout when they aren't 796 | // requested with eth_getFilterChanges for a period of time. 797 | func (eth *EthAPI) UninstallFilter(filter Filter) (bool, error) { 798 | req := eth.requestManager.newRequest("eth_uninstallFilter") 799 | req.Set("params", fmt.Sprintf("0x%x", filter.ID())) 800 | resp, err := eth.requestManager.send(req) 801 | if err != nil { 802 | return false, err 803 | } 804 | 805 | if resp.Error() != nil { 806 | return false, resp.Error() 807 | } 808 | 809 | return resp.Get("result").(bool), nil 810 | } 811 | 812 | // GetFilterChanges polling method for a filter, which returns an array of logs 813 | // which occurred since last poll. 814 | func (eth *EthAPI) GetFilterChanges(filter Filter) (result []interface{}, err error) { 815 | req := eth.requestManager.newRequest("eth_getFilterChanges") 816 | req.Set("params", fmt.Sprintf("0x%x", filter.ID())) 817 | resp, err := eth.requestManager.send(req) 818 | if err != nil { 819 | return nil, err 820 | } 821 | 822 | if resp.Error() != nil { 823 | return nil, resp.Error() 824 | } 825 | 826 | return resp.Get("result").([]interface{}), nil 827 | } 828 | 829 | // GetFilterLogs returns an array of all logs matching filter with given id. 830 | func (eth *EthAPI) GetFilterLogs(filter Filter) (result []interface{}, err error) { 831 | req := eth.requestManager.newRequest("eth_getFilterLogs") 832 | req.Set("params", fmt.Sprintf("0x%x", filter.ID())) 833 | resp, err := eth.requestManager.send(req) 834 | if err != nil { 835 | return nil, err 836 | } 837 | 838 | if resp.Error() != nil { 839 | return nil, resp.Error() 840 | } 841 | 842 | return resp.Get("result").([]interface{}), nil 843 | } 844 | 845 | // GetLogs returns an array of all logs matching a given filter object. 846 | func (eth *EthAPI) GetLogs(filter Filter) (result []interface{}, err error) { 847 | req := eth.requestManager.newRequest("eth_getLogs") 848 | req.Set("params", fmt.Sprintf("0x%x", filter.ID())) 849 | resp, err := eth.requestManager.send(req) 850 | if err != nil { 851 | return nil, err 852 | } 853 | 854 | if resp.Error() != nil { 855 | return nil, resp.Error() 856 | } 857 | 858 | return resp.Get("result").([]interface{}), nil 859 | } 860 | 861 | // GetWork returns the hash of the current block, the seedHash, and the boundary 862 | // condition to be met ("target"). 863 | func (eth *EthAPI) GetWork() (header, seed, boundary common.Hash, err error) { 864 | req := eth.requestManager.newRequest("eth_getWork") 865 | resp, err := eth.requestManager.send(req) 866 | if err != nil { 867 | return common.NewHash(nil), common.NewHash(nil), common.NewHash(nil), err 868 | } 869 | 870 | if resp.Error() != nil { 871 | return common.NewHash(nil), common.NewHash(nil), common.NewHash(nil), resp.Error() 872 | } 873 | 874 | results := resp.Get("result").([]interface{}) 875 | header = common.StringToHash(results[0].(string)) 876 | seed = common.StringToHash(results[1].(string)) 877 | boundary = common.StringToHash(results[2].(string)) 878 | return header, seed, boundary, nil 879 | } 880 | 881 | // SubmitWork is used for submitting a proof-of-work solution. 882 | func (eth *EthAPI) SubmitWork(nonce uint64, header, mixDigest common.Hash) (bool, error) { 883 | req := eth.requestManager.newRequest("eth_submitWork") 884 | req.Set("params", []string{ 885 | fmt.Sprintf("0x%16x", nonce), 886 | header.String(), 887 | mixDigest.String(), 888 | }) 889 | resp, err := eth.requestManager.send(req) 890 | if err != nil { 891 | return false, err 892 | } 893 | 894 | if resp.Error() != nil { 895 | return false, resp.Error() 896 | } 897 | 898 | return resp.Get("result").(bool), nil 899 | } 900 | --------------------------------------------------------------------------------