[WIP] merge proxy command to serve command #50
27
.github/workflows/on-pr.yaml
vendored
27
.github/workflows/on-pr.yaml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
test:
|
||||
name: Run integration tests
|
||||
name: Run unit tests
|
||||
env:
|
||||
GOPATH: /tmp/go
|
||||
strategy:
|
||||
@ -33,3 +33,28 @@ jobs:
|
||||
run: |
|
||||
sleep 10
|
||||
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=127.0.0.1 make test
|
||||
|
||||
integrationtest:
|
||||
name: Run integration tests
|
||||
env:
|
||||
GOPATH: /tmp/go
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.15.x]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Create GOPATH
|
||||
run: mkdir -p /tmp/go
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run database
|
||||
run: docker-compose -f docker-compose.test.yml -f docker-compose.yml up -d db dapptools contract eth-server
|
||||
- name: Test
|
||||
run: |
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8081)" != "200" ]; do echo "waiting for ipld-eth-server..." && sleep 5; done && \
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8545)" != "200" ]; do echo "waiting for geth-statediff..." && sleep 5; done && \
|
||||
make integrationtest
|
||||
|
13
Dockerfile
13
Dockerfile
@ -6,8 +6,17 @@ RUN apk add busybox-extras
|
||||
|
||||
# Build ipld-eth-server
|
||||
WORKDIR /go/src/github.com/vulcanize/ipld-eth-server
|
||||
ADD . .
|
||||
RUN GO111MODULE=on GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o ipld-eth-server .
|
||||
|
||||
# Cache the modules
|
||||
ENV GO111MODULE=on
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
# Build the binary
|
||||
RUN GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o ipld-eth-server .
|
||||
|
||||
# Copy migration tool
|
||||
WORKDIR /
|
||||
|
22
Makefile
22
Makefile
@ -61,39 +61,25 @@ test: | $(GINKGO) $(GOOSE)
|
||||
dropdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) --if-exists $(TEST_DB)
|
||||
createdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) $(TEST_DB)
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" up
|
||||
$(GINKGO) -r --skipPackage=integration_tests,integration
|
||||
$(GINKGO) -r --skipPackage=test
|
||||
|
||||
.PHONY: integrationtest
|
||||
integrationtest: | $(GINKGO) $(GOOSE)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
export PGPASSWORD=$(DATABASE_PASSWORD)
|
||||
dropdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) --if-exists $(TEST_DB)
|
||||
createdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) $(TEST_DB)
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" up
|
||||
$(GINKGO) -r integration_test/
|
||||
$(GINKGO) -r test/ -v
|
||||
|
||||
.PHONY: test_local
|
||||
test_local: | $(GINKGO) $(GOOSE)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
dropdb -h $(HOST_NAME) -p $(PORT) -U $(USER) --if-exists $(TEST_DB)
|
||||
createdb -h $(HOST_NAME) -p $(PORT) -U $(USER) $(TEST_DB)
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" up
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" reset
|
||||
make migrate NAME=$(TEST_DB)
|
||||
$(GINKGO) -r --skipPackage=integration_tests,integration
|
||||
./scripts/run_unit_test.sh
|
||||
|
||||
.PHONY: integrationtest_local
|
||||
integrationtest_local: | $(GINKGO) $(GOOSE)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
dropdb -h $(HOST_NAME) -p $(PORT) -U $(USER) --if-exists $(TEST_DB)
|
||||
createdb -h $(HOST_NAME) -p $(PORT) -U $(USER) $(TEST_DB)
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" up
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" reset
|
||||
make migrate NAME=$(TEST_DB)
|
||||
$(GINKGO) -r integration_test/
|
||||
./scripts/run_intregration_test.sh
|
||||
|
||||
build:
|
||||
go fmt ./...
|
||||
|
26
README.md
26
README.md
@ -121,6 +121,32 @@ The currently supported standard endpoints are:
|
||||
|
||||
TODO: Add the rest of the standard endpoints and unique endpoints (e.g. getSlice)
|
||||
|
||||
|
||||
|
||||
### CLI Options and Environment variables
|
||||
|
||||
|
||||
| CLI Option | Environment Variable | Default Value | Comment |
|
||||
| ----------------------------- | ----------------------------- | ---------------- | ----------------------------------- |
|
||||
| `database-hostname` | `DATABASE_HOSTNAME` | localhost | IPLD database host |
|
||||
| `database-port` | `DATABASE_PORT` | 5432 | IPLD database port |
|
||||
| `database-name` | `DATABASE_NAME` | vulcanize_public | IPLD database name |
|
||||
| `database-user` | `DATABASE_USER` | | IPLD database user |
|
||||
| `database-password` | `DATABASE_PASSWORD` | | IPLD database password |
|
||||
| `eth-server-graphql` | `ETH_SERVER_GRAPHQL` | false | If `true` enable Eth GraphQL Server |
|
||||
| `eth-server-graphql-path` | `ETH_SERVER_GRAPHQLPATH` | | If `eth-server-graphql` set to true, endpoint url for graphql server (host:port) |
|
||||
| `eth-server-http` | `ETH_SERVER_HTTP` | true | If `true` enable Eth HTTP JSON-RPC Server |
|
||||
| `eth-server-http-path` | `ETH_SERVER_HTTPPATH` | | If `eth-server-http` set to `true`, endpoint url for Eth HTTP JSON-RPC server (host:port) |
|
||||
| `eth-server-ws` | `ETH_SERVER_WS` | false | If `true` enable Eth WS JSON-RPC Server |
|
||||
| `eth-server-ws-path` | `ETH_SERVER_WSPATH` | | If `eth-server-ws` set to `true`, endpoint url for Eth WS JSON-RPC server (host:port) |
|
||||
| `eth-server-ipc` | `ETH_SERVER_IPC` | false | If `true` enable Eth IPC JSON-RPC Server |
|
||||
| `eth-server-ipc-path` | `ETH_SERVER_IPC_PATH` | | If `eth-server-ws` set to `true`, path for Eth IPC JSON-RPC server |
|
||||
| `ipld-server-graphql` | `IPLD_SERVER_GRAPHQL` | false | If `true` enable IPLD GraphQL Server |
|
||||
| `ipld-server-graphql-path` | `IPLD_SERVER_GRAPHQLPATH` | | If `ipld-server-graphql` set to true, endpoint url for graphql server (host:port) |
|
||||
| `ipld-postgraphile-path` | `IPLD_POSTGRAPHILEPATH` | | If `ipld-server-graphql` set to true, http url for postgraphile server on top of IPLD db |
|
||||
| `tracing-http-path` | `TRACING_HTTPPATH` | | If `ipld-server-graphql` set to true, http url for tracing server |
|
||||
| `tracing-postgraphile-path` | `TRACING.POSTGRAPHILEPATH` | | If `ipld-server-graphql` set to true, http url for postgraphile server on top of tracing db |
|
||||
|
||||
|
||||
### Testing
|
||||
`make test` will run the unit tests
|
||||
`make test` setups a clean `vulcanize_testing` db
|
||||
|
16
chain.json
Normal file
16
chain.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"chainId": 4,
|
||||
"homesteadBlock": 1,
|
||||
"eip150Block": 2,
|
||||
"eip150Hash": "0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9",
|
||||
"eip155Block": 3,
|
||||
"eip158Block": 3,
|
||||
"byzantiumBlock": 3,
|
||||
"constantinopleBlock": 3,
|
||||
"petersburgBlock": 3,
|
||||
"istanbulBlock": 3,
|
||||
"clique": {
|
||||
"period": 15,
|
||||
"epoch": 30000
|
||||
}
|
||||
}
|
125
cmd/proxy.go
125
cmd/proxy.go
@ -1,125 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/vulcanize/gap-filler/pkg/mux"
|
||||
)
|
||||
|
||||
var ErrNoRpcEndpoints = errors.New("no rpc endpoints is available")
|
||||
|
||||
// proxyCmd represents the proxy command
|
||||
var proxyCmd = &cobra.Command{
|
||||
Use: "proxy",
|
||||
Short: "serve chain data from PG-IPFS or proxy geths",
|
||||
Long: `This command configures a VulcanizeDB ipld-eth-server graphql server.
|
||||
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *logrus.WithField("SubCommand", subCommand)
|
||||
proxy()
|
||||
},
|
||||
}
|
||||
|
||||
func proxy() {
|
||||
gqlDefaultAddr, err := url.Parse(viper.GetString("gql.default"))
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
gqlTracingAPIAddr, err := url.Parse(viper.GetString("gql.tracing"))
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
rpcClients, err := parseRpcAddresses(viper.GetString("rpc.eth"))
|
||||
if err != nil {
|
||||
logrus.Error("bad rpc.eth addresses")
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
tracingClients, err := parseRpcAddresses(viper.GetString("rpc.tracing"))
|
||||
if err != nil {
|
||||
logrus.Error("bad rpc.tracing addresses")
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
router, err := mux.NewServeMux(&mux.Options{
|
||||
BasePath: viper.GetString("http.path"),
|
||||
EnableGraphiQL: viper.GetBool("gql.gui"),
|
||||
Postgraphile: mux.PostgraphileOptions{
|
||||
Default: gqlDefaultAddr,
|
||||
TracingAPI: gqlTracingAPIAddr,
|
||||
},
|
||||
RPC: mux.RPCOptions{
|
||||
DefaultClients: rpcClients,
|
||||
TracingClients: tracingClients,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("%s:%s", viper.GetString("http.host"), viper.GetString("http.port"))
|
||||
if err := http.ListenAndServe(addr, router); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseRpcAddresses(value string) ([]*rpc.Client, error) {
|
||||
rpcAddresses := strings.Split(value, ",")
|
||||
rpcClients := make([]*rpc.Client, 0, len(rpcAddresses))
|
||||
for _, address := range rpcAddresses {
|
||||
rpcClient, err := rpc.Dial(address)
|
||||
if err != nil {
|
||||
logWithCommand.Errorf("couldn't connect to %s. Error: %s", address, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rpcClients = append(rpcClients, rpcClient)
|
||||
}
|
||||
|
||||
if len(rpcClients) == 0 {
|
||||
logWithCommand.Error(ErrNoRpcEndpoints)
|
||||
return nil, ErrNoRpcEndpoints
|
||||
}
|
||||
|
||||
return rpcClients, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(proxyCmd)
|
||||
|
||||
// flags
|
||||
proxyCmd.PersistentFlags().String("http-host", "127.0.0.1", "http host")
|
||||
proxyCmd.PersistentFlags().String("http-port", "8083", "http port")
|
||||
proxyCmd.PersistentFlags().String("http-path", "/", "http base path")
|
||||
|
||||
proxyCmd.PersistentFlags().String("rpc-eth", "http://127.0.0.1:8545", "comma separated ethereum rpc addresses. Example http://127.0.0.1:8545,http://127.0.0.2:8545")
|
||||
proxyCmd.PersistentFlags().String("rpc-tracing", "http://127.0.0.1:8000", "comma separated traicing api addresses")
|
||||
|
||||
proxyCmd.PersistentFlags().String("gql-default", "http://127.0.0.1:5020/graphql", "postgraphile address")
|
||||
proxyCmd.PersistentFlags().String("gql-tracing", "http://127.0.0.1:5020/graphql", "tracing api postgraphile address")
|
||||
proxyCmd.PersistentFlags().Bool("gql-gui", false, "enable graphiql interface")
|
||||
|
||||
// and their .toml config bindings
|
||||
viper.BindPFlag("http.host", proxyCmd.PersistentFlags().Lookup("http-host"))
|
||||
viper.BindPFlag("http.port", proxyCmd.PersistentFlags().Lookup("http-port"))
|
||||
viper.BindPFlag("http.path", proxyCmd.PersistentFlags().Lookup("http-path"))
|
||||
|
||||
viper.BindPFlag("rpc.eth", proxyCmd.PersistentFlags().Lookup("rpc-eth"))
|
||||
viper.BindPFlag("rpc.tracing", proxyCmd.PersistentFlags().Lookup("rpc-tracing"))
|
||||
|
||||
viper.BindPFlag("gql.default", proxyCmd.PersistentFlags().Lookup("gql-default"))
|
||||
viper.BindPFlag("gql.tracing", proxyCmd.PersistentFlags().Lookup("gql-tracing"))
|
||||
viper.BindPFlag("gql.gui", proxyCmd.PersistentFlags().Lookup("gql-gui"))
|
||||
}
|
11
cmd/root.go
11
cmd/root.go
@ -99,11 +99,7 @@ func init() {
|
||||
viper.AutomaticEnv()
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
|
||||
rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
|
||||
rootCmd.PersistentFlags().Int("database-port", 5432, "database port")
|
||||
rootCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname")
|
||||
rootCmd.PersistentFlags().String("database-user", "", "database user")
|
||||
rootCmd.PersistentFlags().String("database-password", "", "database password")
|
||||
|
||||
rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file")
|
||||
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "log level (trace, debug, info, warn, error, fatal, panic)")
|
||||
rootCmd.PersistentFlags().String("log-file", "", "file path for logging")
|
||||
@ -114,11 +110,6 @@ func init() {
|
||||
rootCmd.PersistentFlags().String("prom-http-addr", "127.0.0.1", "http host for prometheus")
|
||||
rootCmd.PersistentFlags().String("prom-http-port", "8090", "http port for prometheus")
|
||||
|
||||
viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name"))
|
||||
viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port"))
|
||||
viper.BindPFlag("database.hostname", rootCmd.PersistentFlags().Lookup("database-hostname"))
|
||||
viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user"))
|
||||
viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password"))
|
||||
viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
|
||||
viper.BindPFlag("log.file", rootCmd.PersistentFlags().Lookup("log-file"))
|
||||
|
||||
|
206
cmd/serve.go
206
cmd/serve.go
@ -16,24 +16,29 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/graphql"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/gap-filler/pkg/mux"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/graphql"
|
||||
srpc "github.com/vulcanize/ipld-eth-server/pkg/rpc"
|
||||
s "github.com/vulcanize/ipld-eth-server/pkg/serve"
|
||||
v "github.com/vulcanize/ipld-eth-server/version"
|
||||
)
|
||||
|
||||
var ErrNoRpcEndpoints = errors.New("no rpc endpoints is available")
|
||||
|
||||
// serveCmd represents the serve command
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
@ -71,7 +76,12 @@ func serve() {
|
||||
if err := startServers(server, serverConfig); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
graphQL, err := startGraphQL(server)
|
||||
graphQL, err := startEthGraphQL(server, serverConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
err = startIpldGraphQL(serverConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
@ -87,27 +97,43 @@ func serve() {
|
||||
}
|
||||
|
||||
func startServers(server s.Server, settings *s.Config) error {
|
||||
logWithCommand.Info("starting up IPC server")
|
||||
_, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
|
||||
if err != nil {
|
||||
return err
|
||||
if settings.IPCEnabled {
|
||||
logWithCommand.Info("starting up IPC server")
|
||||
_, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("IPC server is disabled")
|
||||
}
|
||||
logWithCommand.Info("starting up WS server")
|
||||
_, _, err = srpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb"}, nil, true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if settings.WSEnabled {
|
||||
logWithCommand.Info("starting up WS server")
|
||||
_, _, err := srpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb", "net"}, nil, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("WS server is disabled")
|
||||
}
|
||||
logWithCommand.Info("starting up HTTP server")
|
||||
_, err = srpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"eth", "net"}, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
||||
return err
|
||||
|
||||
if settings.HTTPEnabled {
|
||||
logWithCommand.Info("starting up HTTP server")
|
||||
_, err := srpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"eth", "net"}, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("HTTP server is disabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startGraphQL(server s.Server) (graphQLServer *graphql.Service, err error) {
|
||||
viper.BindEnv("server.graphql", "SERVER_GRAPHQL")
|
||||
if viper.GetBool("server.graphql") {
|
||||
logWithCommand.Info("starting up GraphQL server")
|
||||
viper.BindEnv("server.graphqlEndpoint", "SERVER_GRAPHQL_ENDPOINT")
|
||||
endPoint := viper.GetString("server.graphqlEndpoint")
|
||||
func startEthGraphQL(server s.Server, settings *s.Config) (graphQLServer *graphql.Service, err error) {
|
||||
if settings.EthGraphqlEnabled {
|
||||
logWithCommand.Info("starting up ETH GraphQL server")
|
||||
endPoint := settings.EthGraphqlEndpoint
|
||||
if endPoint != "" {
|
||||
graphQLServer, err = graphql.New(server.Backend(), endPoint, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
||||
if err != nil {
|
||||
@ -115,19 +141,113 @@ func startGraphQL(server s.Server) (graphQLServer *graphql.Service, err error) {
|
||||
}
|
||||
err = graphQLServer.Start(nil)
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("ETH GraphQL server is disabled")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func startIpldGraphQL(settings *s.Config) error {
|
||||
if settings.IpldGraphqlEnabled {
|
||||
logWithCommand.Info("starting up IPLD GraphQL server")
|
||||
|
||||
gqlIpldAddr, err := url.Parse(settings.IpldPostgraphileEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gqlTracingAPIAddr, err := url.Parse(settings.TracingPostgraphileEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ethClients, err := parseRpcAddresses(settings.EthHttpEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tracingClients []*rpc.Client
|
||||
tracingEndpoint := viper.GetString("tracing.httpPath")
|
||||
if tracingEndpoint != "" {
|
||||
tracingClients, err = parseRpcAddresses(tracingEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
router, err := mux.NewServeMux(&mux.Options{
|
||||
BasePath: "/",
|
||||
EnableGraphiQL: true,
|
||||
Postgraphile: mux.PostgraphileOptions{
|
||||
Default: gqlIpldAddr,
|
||||
TracingAPI: gqlTracingAPIAddr,
|
||||
},
|
||||
RPC: mux.RPCOptions{
|
||||
DefaultClients: ethClients,
|
||||
TracingClients: tracingClients,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go http.ListenAndServe(settings.IpldGraphqlEndpoint, router)
|
||||
} else {
|
||||
logWithCommand.Info("IPLD GraphQL server is disabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRpcAddresses(value string) ([]*rpc.Client, error) {
|
||||
rpcAddresses := strings.Split(value, ",")
|
||||
rpcClients := make([]*rpc.Client, 0, len(rpcAddresses))
|
||||
for _, address := range rpcAddresses {
|
||||
rpcClient, err := rpc.Dial(address)
|
||||
if err != nil {
|
||||
logWithCommand.Errorf("couldn't connect to %s. Error: %s", address, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rpcClients = append(rpcClients, rpcClient)
|
||||
}
|
||||
|
||||
if len(rpcClients) == 0 {
|
||||
logWithCommand.Error(ErrNoRpcEndpoints)
|
||||
return nil, ErrNoRpcEndpoints
|
||||
}
|
||||
|
||||
return rpcClients, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
|
||||
// database credentials
|
||||
serveCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
|
||||
serveCmd.PersistentFlags().Int("database-port", 5432, "database port")
|
||||
serveCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname")
|
||||
serveCmd.PersistentFlags().String("database-user", "", "database user")
|
||||
serveCmd.PersistentFlags().String("database-password", "", "database password")
|
||||
|
||||
// flags for all config variables
|
||||
serveCmd.PersistentFlags().Bool("server-graphql", false, "turn on the graphql server")
|
||||
serveCmd.PersistentFlags().String("server-graphql-endpoint", "", "endpoint url for graphql server")
|
||||
serveCmd.PersistentFlags().String("server-ws-path", "", "vdb server ws path")
|
||||
serveCmd.PersistentFlags().String("server-http-path", "", "vdb server http path")
|
||||
serveCmd.PersistentFlags().String("server-ipc-path", "", "vdb server ipc path")
|
||||
// eth graphql and json-rpc parameters
|
||||
serveCmd.PersistentFlags().Bool("eth-server-graphql", false, "turn on the eth graphql server")
|
||||
serveCmd.PersistentFlags().String("eth-server-graphql-path", "", "endpoint url for eth graphql server (host:port)")
|
||||
serveCmd.PersistentFlags().Bool("eth-server-http", true, "turn on the eth http json-rpc server")
|
||||
serveCmd.PersistentFlags().String("eth-server-http-path", "", "endpoint url for eth http json-rpc server (host:port)")
|
||||
serveCmd.PersistentFlags().Bool("eth-server-ws", false, "turn on the eth websocket json-rpc server")
|
||||
serveCmd.PersistentFlags().String("eth-server-ws-path", "", "endpoint url for eth websocket json-rpc server (host:port)")
|
||||
serveCmd.PersistentFlags().Bool("eth-server-ipc", false, "turn on the eth ipc json-rpc server")
|
||||
serveCmd.PersistentFlags().String("eth-server-ipc-path", "", "path for eth ipc json-rpc server")
|
||||
|
||||
// ipld and tracing graphql parameters
|
||||
serveCmd.PersistentFlags().Bool("ipld-server-graphql", false, "turn on the ipld graphql server")
|
||||
serveCmd.PersistentFlags().String("ipld-server-graphql-path", "", "endpoint url for ipld graphql server (host:port)")
|
||||
serveCmd.PersistentFlags().String("ipld-postgraphile-path", "", "http url to postgraphile on top of ipld database")
|
||||
serveCmd.PersistentFlags().String("tracing-http-path", "", "http url to tracing service")
|
||||
serveCmd.PersistentFlags().String("tracing-postgraphile-path", "", "http url to postgraphile on top of tracing db")
|
||||
|
||||
serveCmd.PersistentFlags().String("eth-http-path", "", "http url for ethereum node")
|
||||
serveCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
|
||||
@ -141,11 +261,35 @@ func init() {
|
||||
serveCmd.PersistentFlags().Bool("eth-supports-state-diff", false, "whether or not the proxy ethereum client supports statediffing endpoints")
|
||||
|
||||
// and their bindings
|
||||
viper.BindPFlag("server.graphql", serveCmd.PersistentFlags().Lookup("server-graphql"))
|
||||
viper.BindPFlag("server.graphqlEndpoint", serveCmd.PersistentFlags().Lookup("server-graphql-endpoint"))
|
||||
viper.BindPFlag("server.wsPath", serveCmd.PersistentFlags().Lookup("server-ws-path"))
|
||||
viper.BindPFlag("server.httpPath", serveCmd.PersistentFlags().Lookup("server-http-path"))
|
||||
viper.BindPFlag("server.ipcPath", serveCmd.PersistentFlags().Lookup("server-ipc-path"))
|
||||
// database
|
||||
viper.BindPFlag("database.name", serveCmd.PersistentFlags().Lookup("database-name"))
|
||||
viper.BindPFlag("database.port", serveCmd.PersistentFlags().Lookup("database-port"))
|
||||
viper.BindPFlag("database.hostname", serveCmd.PersistentFlags().Lookup("database-hostname"))
|
||||
viper.BindPFlag("database.user", serveCmd.PersistentFlags().Lookup("database-user"))
|
||||
viper.BindPFlag("database.password", serveCmd.PersistentFlags().Lookup("database-password"))
|
||||
|
||||
// eth graphql server
|
||||
viper.BindPFlag("eth.server.graphql", serveCmd.PersistentFlags().Lookup("eth-server-graphql"))
|
||||
viper.BindPFlag("eth.server.graphqlPath", serveCmd.PersistentFlags().Lookup("eth-server-graphql-path"))
|
||||
|
||||
// eth http json-rpc server
|
||||
viper.BindPFlag("eth.server.http", serveCmd.PersistentFlags().Lookup("eth-server-http"))
|
||||
viper.BindPFlag("eth.server.httpPath", serveCmd.PersistentFlags().Lookup("eth-server-http-path"))
|
||||
|
||||
// eth websocket json-rpc server
|
||||
viper.BindPFlag("eth.server.ws", serveCmd.PersistentFlags().Lookup("eth-server-ws"))
|
||||
viper.BindPFlag("eth.server.wsPath", serveCmd.PersistentFlags().Lookup("eth-server-ws-path"))
|
||||
|
||||
// eth ipc json-rpc server
|
||||
viper.BindPFlag("eth.server.ipc", serveCmd.PersistentFlags().Lookup("eth-server-ipc"))
|
||||
viper.BindPFlag("eth.server.ipcPath", serveCmd.PersistentFlags().Lookup("eth-server-ipc-path"))
|
||||
|
||||
// ipld and tracing graphql parameters
|
||||
viper.BindPFlag("ipld.server.graphql", serveCmd.PersistentFlags().Lookup("ipld-server-graphql"))
|
||||
viper.BindPFlag("ipld.server.graphqlPath", serveCmd.PersistentFlags().Lookup("ipld-server-graphql-path"))
|
||||
viper.BindPFlag("ipld.postgraphilePath", serveCmd.PersistentFlags().Lookup("ipld-postgraphile-path"))
|
||||
viper.BindPFlag("tracing.httpPath", serveCmd.PersistentFlags().Lookup("tracing-http-path"))
|
||||
viper.BindPFlag("tracing.postgraphilePath", serveCmd.PersistentFlags().Lookup("tracing-postgraphile-path"))
|
||||
|
||||
viper.BindPFlag("ethereum.httpPath", serveCmd.PersistentFlags().Lookup("eth-http-path"))
|
||||
viper.BindPFlag("ethereum.nodeID", serveCmd.PersistentFlags().Lookup("eth-node-id"))
|
||||
|
14
docker-compose.test.yml
Normal file
14
docker-compose.test.yml
Normal file
@ -0,0 +1,14 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
contract:
|
||||
depends_on:
|
||||
- dapptools
|
||||
build:
|
||||
context: ./test/contract
|
||||
args:
|
||||
ETH_ADDR: "http://dapptools:8545"
|
||||
environment:
|
||||
ETH_ADDR: "http://dapptools:8545"
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
@ -3,11 +3,31 @@ version: '3.2'
|
||||
services:
|
||||
dapptools:
|
||||
restart: unless-stopped
|
||||
image: vulcanize/dapptools:v0.29.0-statediff-0.0.2
|
||||
depends_on:
|
||||
- statediff-migrations
|
||||
image: vulcanize/dapptools:v0.29.0-v1.10.2-statediff-0.0.19
|
||||
environment:
|
||||
DB_USER: vdbm
|
||||
DB_NAME: vulcanize_public
|
||||
DB_HOST: db
|
||||
DB_PORT: 5432
|
||||
DB_PASSWORD: password
|
||||
ports:
|
||||
- "127.0.0.1:8545:8545"
|
||||
- "127.0.0.1:8546:8546"
|
||||
|
||||
statediff-migrations:
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- db
|
||||
image: vulcanize/statediff-migrations:v0.4.0
|
||||
environment:
|
||||
DATABASE_USER: vdbm
|
||||
DATABASE_NAME: vulcanize_public
|
||||
DATABASE_HOSTNAME: db
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_PASSWORD: password
|
||||
|
||||
db:
|
||||
restart: always
|
||||
image: postgres:10.12-alpine
|
||||
@ -20,25 +40,8 @@ services:
|
||||
ports:
|
||||
- "127.0.0.1:8077:5432"
|
||||
|
||||
eth-indexer:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
- dapptools
|
||||
image: vulcanize/ipld-eth-indexer:v0.3.0-alpha
|
||||
environment:
|
||||
DATABASE_NAME: vulcanize_public
|
||||
DATABASE_HOSTNAME: db
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: vdbm
|
||||
DATABASE_PASSWORD: password
|
||||
ETH_WS_PATH: "dapptools:8546"
|
||||
ETH_HTTP_PATH: "dapptools:8545"
|
||||
ETH_CHAIN_ID: 4
|
||||
ETH_NETWORK_ID: 4
|
||||
VDB_COMMAND: sync
|
||||
|
||||
eth-server:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
@ -47,16 +50,22 @@ services:
|
||||
- alpine:latest
|
||||
- golang:1.13-alpine
|
||||
environment:
|
||||
IPLD_SERVER_GRAPHQL: "true"
|
||||
IPLD_POSTGRAPHILEPATH: http://graphql:5000
|
||||
ETH_SERVER_HTTPPATH: 0.0.0.0:8081
|
||||
VDB_COMMAND: "serve"
|
||||
ETH_CHAIN_CONFIG: "/tmp/chain.json"
|
||||
DATABASE_NAME: "vulcanize_public"
|
||||
DATABASE_HOSTNAME: "db"
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_PASSWORD: "password"
|
||||
SERVER_WS_PATH: "0.0.0.0:8081"
|
||||
SERVER_HTTP_PATH: "0.0.0.0:8082"
|
||||
ETH_CHAIN_ID: 4
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./chain.json
|
||||
target: /tmp/chain.json
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080"
|
||||
- "127.0.0.1:8081:8081"
|
||||
|
||||
graphql:
|
||||
|
175
pkg/eth/api.go
175
pkg/eth/api.go
@ -18,9 +18,11 @@ package eth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
@ -38,8 +40,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||
)
|
||||
|
||||
@ -168,6 +168,21 @@ func (pea *PublicEthAPI) GetBlockByHash(ctx context.Context, hash common.Hash, f
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config.
|
||||
func (pea *PublicEthAPI) ChainId() hexutil.Uint64 {
|
||||
chainID := new(big.Int)
|
||||
block, err := pea.B.CurrentBlock()
|
||||
if err != nil {
|
||||
logrus.Errorf("ChainId failed with err %s", err.Error())
|
||||
|
||||
return 0
|
||||
}
|
||||
if config := pea.B.Config.ChainConfig; config.IsEIP155(block.Number()) {
|
||||
chainID = config.ChainID
|
||||
}
|
||||
return (hexutil.Uint64)(chainID.Uint64())
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Uncles
|
||||
@ -441,6 +456,14 @@ func (pea *PublicEthAPI) localGetTransactionReceipt(ctx context.Context, hash co
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, err := pea.B.BlockByHash(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = receipts.DeriveFields(pea.B.Config.ChainConfig, blockHash, blockNumber, block.Transactions())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(receipts) <= int(index) {
|
||||
return nil, nil
|
||||
}
|
||||
@ -526,9 +549,11 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
||||
for i, addr := range crit.Addresses {
|
||||
addrStrs[i] = addr.String()
|
||||
}
|
||||
topicStrSets := make([][]string, 4)
|
||||
|
||||
topicStrSets := make([][]string, len(crit.Topics))
|
||||
for i, topicSet := range crit.Topics {
|
||||
if i > 3 {
|
||||
topicStrSets = topicStrSets[:4]
|
||||
// don't allow more than 4 topics
|
||||
break
|
||||
}
|
||||
@ -541,14 +566,42 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
||||
Topics: topicStrSets,
|
||||
}
|
||||
|
||||
// Begin tx
|
||||
tx, err := pea.B.DB.Beginx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
// If we have a blockhash to filter on, fire off single retrieval query
|
||||
if crit.BlockHash != nil {
|
||||
blocks, topics, err := pea.getLogsForBlockHash(filter, crit.BlockHash)
|
||||
rctCIDs, err := pea.B.Retriever.RetrieveRctCIDs(tx, filter, 0, crit.BlockHash, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return extractLogsOfInterest(blocks, topics)
|
||||
rctIPLDs, err := pea.B.Fetcher.FetchRcts(tx, rctCIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, err := pea.B.BlockByHash(context.Background(), *crit.BlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return extractLogsOfInterest(pea.B.Config.ChainConfig, *crit.BlockHash, block.NumberU64(), block.Transactions(), rctIPLDs, filter)
|
||||
}
|
||||
|
||||
// Otherwise, create block range from criteria
|
||||
// nil values are filled in; to request a single block have both ToBlock and FromBlock equal that number
|
||||
startingBlock := crit.FromBlock
|
||||
@ -556,6 +609,7 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
||||
if startingBlock == nil {
|
||||
startingBlock = common.Big0
|
||||
}
|
||||
|
||||
if endingBlock == nil {
|
||||
endingBlockInt, err := pea.B.Retriever.RetrieveLastBlockNumber()
|
||||
if err != nil {
|
||||
@ -563,71 +617,39 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
||||
}
|
||||
endingBlock = big.NewInt(endingBlockInt)
|
||||
}
|
||||
|
||||
start := startingBlock.Int64()
|
||||
end := endingBlock.Int64()
|
||||
blocks, topics, err := pea.getLogsForBlockRange(filter, start, end)
|
||||
if err != nil {
|
||||
var logs []*types.Log
|
||||
for i := start; i <= end; i++ {
|
||||
rctCIDs, err := pea.B.Retriever.RetrieveRctCIDs(tx, filter, i, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := pea.B.BlockByNumber(context.Background(), rpc.BlockNumber(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rctIPLDs, err := pea.B.Fetcher.FetchRcts(tx, rctCIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log, err := extractLogsOfInterest(pea.B.Config.ChainConfig, block.Hash(), uint64(i), block.Transactions(), rctIPLDs, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logs = append(logs, log...)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return extractLogsOfInterest(blocks, topics)
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) getLogsForBlockRange(filter ReceiptFilter, start, end int64) ([]ipfs.BlockModel, [][]string, error) {
|
||||
tx, err := pea.B.DB.Beginx()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
allRctCIDs := make([]eth.ReceiptModel, 0)
|
||||
for i := start; i <= end; i++ {
|
||||
var rctCIDs []eth.ReceiptModel
|
||||
rctCIDs, err = pea.B.Retriever.RetrieveRctCIDs(tx, filter, i, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
allRctCIDs = append(allRctCIDs, rctCIDs...)
|
||||
}
|
||||
var rctIPLDs []ipfs.BlockModel
|
||||
rctIPLDs, err = pea.B.Fetcher.FetchRcts(tx, allRctCIDs)
|
||||
|
||||
return rctIPLDs, filter.Topics, err
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) getLogsForBlockHash(filter ReceiptFilter, blockHash *common.Hash) ([]ipfs.BlockModel, [][]string, error) {
|
||||
tx, err := pea.B.DB.Beginx()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
var rctCIDs []eth.ReceiptModel
|
||||
rctCIDs, err = pea.B.Retriever.RetrieveRctCIDs(tx, filter, 0, blockHash, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var rctIPLDs []ipfs.BlockModel
|
||||
rctIPLDs, err = pea.B.Fetcher.FetchRcts(tx, rctCIDs)
|
||||
|
||||
return rctIPLDs, filter.Topics, err
|
||||
return logs, err // need to return err variable so that we return the err = tx.Commit() assignment in the defer
|
||||
}
|
||||
|
||||
/*
|
||||
@ -651,6 +673,10 @@ func (pea *PublicEthAPI) GetBalance(ctx context.Context, address common.Address,
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
if err == sql.ErrNoRows {
|
||||
return (*hexutil.Big)(big.NewInt(0)), nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -668,7 +694,17 @@ func (pea *PublicEthAPI) localGetBalance(ctx context.Context, address common.Add
|
||||
func (pea *PublicEthAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||
storageVal, err := pea.B.GetStorageByNumberOrHash(ctx, address, common.HexToHash(key), blockNrOrHash)
|
||||
if storageVal != nil && err == nil {
|
||||
return storageVal, nil
|
||||
var value common.Hash
|
||||
_, content, _, err := rlp.Split(storageVal)
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
return hexutil.Bytes{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value.SetBytes(content)
|
||||
|
||||
return value[:], nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var res hexutil.Bytes
|
||||
@ -677,6 +713,9 @@ func (pea *PublicEthAPI) GetStorageAt(ctx context.Context, address common.Addres
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
if err == sql.ErrNoRows {
|
||||
return make([]byte, 32), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -693,6 +732,10 @@ func (pea *PublicEthAPI) GetCode(ctx context.Context, address common.Address, bl
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
if err == sql.ErrNoRows {
|
||||
return code, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,16 @@ package eth_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
. "github.com/onsi/ginkgo"
|
||||
@ -168,7 +171,7 @@ var (
|
||||
"to": expectedTransaction3.To,
|
||||
"gasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].GasUsed),
|
||||
"cumulativeGasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].CumulativeGasUsed),
|
||||
"contractAddress": nil,
|
||||
"contractAddress": test_helpers.ContractAddress,
|
||||
"logs": test_helpers.MockReceipts[2].Logs,
|
||||
"logsBloom": test_helpers.MockReceipts[2].Bloom,
|
||||
"root": hexutil.Bytes(test_helpers.MockReceipts[2].PostState),
|
||||
@ -177,8 +180,9 @@ var (
|
||||
|
||||
var _ = Describe("API", func() {
|
||||
var (
|
||||
db *postgres.DB
|
||||
api *eth.PublicEthAPI
|
||||
db *postgres.DB
|
||||
api *eth.PublicEthAPI
|
||||
chainConfig = params.TestChainConfig
|
||||
)
|
||||
// Test db setup, rather than using BeforeEach we only need to setup once since the tests do not mutate the database
|
||||
// Note: if you focus one of the tests be sure to focus this and the defered It()
|
||||
@ -187,7 +191,11 @@ var _ = Describe("API", func() {
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
indexAndPublisher := eth2.NewIPLDPublisher(db)
|
||||
backend, err := eth.NewEthBackend(db, ð.Config{})
|
||||
backend, err := eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VmConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api = eth.NewPublicEthAPI(backend, nil, false)
|
||||
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
|
||||
@ -271,10 +279,10 @@ var _ = Describe("API", func() {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
})
|
||||
It("Throws an error if a block cannot be found", func() {
|
||||
_, err := api.GetBlockByNumber(ctx, wrongNumber, false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns `nil` if a block cannot be found", func() {
|
||||
block, err := api.GetBlockByNumber(ctx, wrongNumber, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block).To(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
@ -303,10 +311,10 @@ var _ = Describe("API", func() {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
})
|
||||
It("Throws an error if a block cannot be found", func() {
|
||||
_, err := api.GetBlockByHash(ctx, randomHash, false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns `nil` if a block cannot be found", func() {
|
||||
block, err := api.GetBlockByHash(ctx, randomHash, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block).To(BeZero())
|
||||
})
|
||||
})
|
||||
|
||||
@ -325,10 +333,10 @@ var _ = Describe("API", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle2).To(Equal(expectedUncle2))
|
||||
})
|
||||
It("Throws an error if an block for blocknumber cannot be found", func() {
|
||||
_, err := api.GetUncleByBlockNumberAndIndex(ctx, wrongNumber, 0)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns `nil` if an block for block number cannot be found", func() {
|
||||
block, err := api.GetUncleByBlockNumberAndIndex(ctx, wrongNumber, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block).To(BeNil())
|
||||
})
|
||||
It("Returns `nil` if an uncle at the provided index does not exist for the block found for the provided block number", func() {
|
||||
uncle, err := api.GetUncleByBlockNumberAndIndex(ctx, number, 2)
|
||||
@ -346,10 +354,10 @@ var _ = Describe("API", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle2).To(Equal(expectedUncle2))
|
||||
})
|
||||
It("Throws an error if an block for blockhash cannot be found", func() {
|
||||
_, err := api.GetUncleByBlockHashAndIndex(ctx, randomHash, 0)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns `nil` if a block for blockhash cannot be found", func() {
|
||||
block, err := api.GetUncleByBlockHashAndIndex(ctx, randomHash, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block).To(BeNil())
|
||||
})
|
||||
It("Returns `nil` if an uncle at the provided index does not exist for the block with the provided hash", func() {
|
||||
uncle, err := api.GetUncleByBlockHashAndIndex(ctx, blockHash, 2)
|
||||
@ -361,6 +369,7 @@ var _ = Describe("API", func() {
|
||||
Describe("eth_getUncleCountByBlockNumber", func() {
|
||||
It("Retrieves the number of uncles for the canonical block with the provided number", func() {
|
||||
count := api.GetUncleCountByBlockNumber(ctx, number)
|
||||
Expect(*count).NotTo(Equal(nil))
|
||||
Expect(uint64(*count)).To(Equal(uint64(2)))
|
||||
})
|
||||
})
|
||||
@ -368,6 +377,7 @@ var _ = Describe("API", func() {
|
||||
Describe("eth_getUncleCountByBlockHash", func() {
|
||||
It("Retrieves the number of uncles for the block with the provided hash", func() {
|
||||
count := api.GetUncleCountByBlockHash(ctx, blockHash)
|
||||
Expect(*count).NotTo(Equal(nil))
|
||||
Expect(uint64(*count)).To(Equal(uint64(2)))
|
||||
})
|
||||
})
|
||||
@ -948,8 +958,17 @@ var _ = Describe("API", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the balance for", func() {
|
||||
_, err := api.GetBalance(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
It("Retrieves the eth balance for the non-existing account address at the block with the provided hash", func() {
|
||||
bal, err := api.GetBalance(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
})
|
||||
It("Throws an error for an account of a non-existing block hash", func() {
|
||||
_, err := api.GetBalance(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithHash(randomHash, true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("Throws an error for an account of a non-existing block number", func() {
|
||||
_, err := api.GetBalance(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithNumber(wrongNumber))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
@ -965,9 +984,10 @@ var _ = Describe("API", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the code for", func() {
|
||||
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
It("Returns `nil` for an account it cannot find the code for", func() {
|
||||
code, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -18,6 +18,7 @@ package eth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
@ -51,6 +52,8 @@ import (
|
||||
var (
|
||||
errPendingBlockNumber = errors.New("pending block number not supported")
|
||||
errNegativeBlockNumber = errors.New("negative block number not supported")
|
||||
errHeaderHashNotFound = errors.New("header for hash not found")
|
||||
errHeaderNotFound = errors.New("header not found")
|
||||
)
|
||||
|
||||
const (
|
||||
@ -175,7 +178,11 @@ func (b *Backend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Bl
|
||||
if header == nil {
|
||||
return nil, errors.New("header for hash not found")
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && b.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
|
||||
return nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
return header, nil
|
||||
@ -198,9 +205,9 @@ func (b *Backend) GetTd(blockHash common.Hash) (*big.Int, error) {
|
||||
}
|
||||
|
||||
// CurrentBlock returns the current block
|
||||
func (b *Backend) CurrentBlock() *types.Block {
|
||||
block, _ := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber)
|
||||
return block
|
||||
func (b *Backend) CurrentBlock() (*types.Block, error) {
|
||||
block, err := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber)
|
||||
return block, err
|
||||
}
|
||||
|
||||
// BlockByNumberOrHash returns block by number or hash
|
||||
@ -216,7 +223,11 @@ func (b *Backend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Blo
|
||||
if header == nil {
|
||||
return nil, errors.New("header for hash not found")
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && b.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
|
||||
return nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
block, err := b.BlockByHash(ctx, hash)
|
||||
@ -254,14 +265,20 @@ func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber
|
||||
return nil, errNegativeBlockNumber
|
||||
}
|
||||
// Get the canonical hash
|
||||
canonicalHash := b.GetCanonicalHash(uint64(number))
|
||||
canonicalHash, err := b.GetCanonicalHash(uint64(number))
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
// Retrieve all the CIDs for the block
|
||||
// TODO: optimize this by retrieving iplds directly rather than the cids first (this is remanent from when we fetched iplds through ipfs blockservice interface)
|
||||
headerCID, uncleCIDs, txCIDs, rctCIDs, err := b.Retriever.RetrieveBlockByHash(canonicalHash)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -285,6 +302,9 @@ func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber
|
||||
var headerIPLD ipfs.BlockModel
|
||||
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var header types.Header
|
||||
@ -347,6 +367,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
||||
// Retrieve all the CIDs for the block
|
||||
headerCID, uncleCIDs, txCIDs, rctCIDs, err := b.Retriever.RetrieveBlockByHash(hash)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -370,6 +393,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
||||
var headerIPLD ipfs.BlockModel
|
||||
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var header types.Header
|
||||
@ -381,6 +407,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
||||
var uncleIPLDs []ipfs.BlockModel
|
||||
uncleIPLDs, err = b.Fetcher.FetchUncles(tx, uncleCIDs)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var uncles []*types.Header
|
||||
@ -396,6 +425,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
||||
var txIPLDs []ipfs.BlockModel
|
||||
txIPLDs, err = b.Fetcher.FetchTrxs(tx, txCIDs)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var transactions []*types.Transaction
|
||||
@ -411,6 +443,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
||||
var rctIPLDs []ipfs.BlockModel
|
||||
rctIPLDs, err = b.Fetcher.FetchRcts(tx, rctCIDs)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var receipts []*types.Receipt
|
||||
@ -492,7 +527,11 @@ func (b *Backend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHas
|
||||
if header == nil {
|
||||
return nil, nil, errors.New("header for hash not found")
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && b.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
|
||||
return nil, nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
stateDb, err := state.New(header.Root, b.StateDatabase, nil)
|
||||
@ -520,12 +559,12 @@ func (b *Backend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNu
|
||||
}
|
||||
|
||||
// GetCanonicalHash gets the canonical hash for the provided number, if there is one
|
||||
func (b *Backend) GetCanonicalHash(number uint64) common.Hash {
|
||||
func (b *Backend) GetCanonicalHash(number uint64) (common.Hash, error) {
|
||||
var hashResult string
|
||||
if err := b.DB.Get(&hashResult, RetrieveCanonicalBlockHashByNumber, number); err != nil {
|
||||
return common.Hash{}
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return common.HexToHash(hashResult)
|
||||
return common.HexToHash(hashResult), nil
|
||||
}
|
||||
|
||||
type rowResult struct {
|
||||
@ -550,7 +589,7 @@ func (b *Backend) GetEVM(ctx context.Context, msg core.Message, state *state.Sta
|
||||
// GetAccountByNumberOrHash returns the account object for the provided address at the block corresponding to the provided number or hash
|
||||
func (b *Backend) GetAccountByNumberOrHash(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*state.Account, error) {
|
||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||
return b.GetAccountByNumber(ctx, address, uint64(blockNr.Int64()))
|
||||
return b.GetAccountByNumber(ctx, address, blockNr)
|
||||
}
|
||||
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
return b.GetAccountByHash(ctx, address, hash)
|
||||
@ -559,20 +598,48 @@ func (b *Backend) GetAccountByNumberOrHash(ctx context.Context, address common.A
|
||||
}
|
||||
|
||||
// GetAccountByNumber returns the account object for the provided address at the canonical block at the provided height
|
||||
func (b *Backend) GetAccountByNumber(ctx context.Context, address common.Address, number uint64) (*state.Account, error) {
|
||||
hash := b.GetCanonicalHash(number)
|
||||
if hash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
|
||||
func (b *Backend) GetAccountByNumber(ctx context.Context, address common.Address, blockNumber rpc.BlockNumber) (*state.Account, error) {
|
||||
var err error
|
||||
number := blockNumber.Int64()
|
||||
if blockNumber == rpc.LatestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveLastBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.EarliestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveFirstBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.PendingBlockNumber {
|
||||
return nil, errPendingBlockNumber
|
||||
}
|
||||
hash, err := b.GetCanonicalHash(uint64(number))
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errHeaderNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.GetAccountByHash(ctx, address, hash)
|
||||
}
|
||||
|
||||
// GetAccountByHash returns the account object for the provided address at the block with the provided hash
|
||||
func (b *Backend) GetAccountByHash(ctx context.Context, address common.Address, hash common.Hash) (*state.Account, error) {
|
||||
_, err := b.HeaderByHash(context.Background(), hash)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errHeaderHashNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, accountRlp, err := b.IPLDRetriever.RetrieveAccountByAddressAndBlockHash(address, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acct := new(state.Account)
|
||||
return acct, rlp.DecodeBytes(accountRlp, acct)
|
||||
}
|
||||
@ -580,7 +647,7 @@ func (b *Backend) GetAccountByHash(ctx context.Context, address common.Address,
|
||||
// GetCodeByNumberOrHash returns the byte code for the contract deployed at the provided address at the block with the provided hash or block number
|
||||
func (b *Backend) GetCodeByNumberOrHash(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) ([]byte, error) {
|
||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||
return b.GetCodeByNumber(ctx, address, uint64(blockNr.Int64()))
|
||||
return b.GetCodeByNumber(ctx, address, blockNr)
|
||||
}
|
||||
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
return b.GetCodeByHash(ctx, address, hash)
|
||||
@ -589,8 +656,28 @@ func (b *Backend) GetCodeByNumberOrHash(ctx context.Context, address common.Addr
|
||||
}
|
||||
|
||||
// GetCodeByNumber returns the byte code for the contract deployed at the provided address at the canonical block with the provided block number
|
||||
func (b *Backend) GetCodeByNumber(ctx context.Context, address common.Address, number uint64) ([]byte, error) {
|
||||
hash := b.GetCanonicalHash(number)
|
||||
func (b *Backend) GetCodeByNumber(ctx context.Context, address common.Address, blockNumber rpc.BlockNumber) ([]byte, error) {
|
||||
var err error
|
||||
number := blockNumber.Int64()
|
||||
if blockNumber == rpc.LatestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveLastBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.EarliestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveFirstBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.PendingBlockNumber {
|
||||
return nil, errPendingBlockNumber
|
||||
}
|
||||
hash, err := b.GetCanonicalHash(uint64(number))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
|
||||
}
|
||||
@ -631,28 +718,55 @@ func (b *Backend) GetCodeByHash(ctx context.Context, address common.Address, has
|
||||
}
|
||||
|
||||
// GetStorageByNumberOrHash returns the storage value for the provided contract address an storage key at the block corresponding to the provided number or hash
|
||||
func (b *Backend) GetStorageByNumberOrHash(ctx context.Context, address common.Address, storageLeafKey common.Hash, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||
func (b *Backend) GetStorageByNumberOrHash(ctx context.Context, address common.Address, key common.Hash, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||
return b.GetStorageByNumber(ctx, address, storageLeafKey, uint64(blockNr.Int64()))
|
||||
return b.GetStorageByNumber(ctx, address, key, blockNr)
|
||||
}
|
||||
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
return b.GetStorageByHash(ctx, address, storageLeafKey, hash)
|
||||
return b.GetStorageByHash(ctx, address, key, hash)
|
||||
}
|
||||
return nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||
}
|
||||
|
||||
// GetStorageByNumber returns the storage value for the provided contract address an storage key at the block corresponding to the provided number
|
||||
func (b *Backend) GetStorageByNumber(ctx context.Context, address common.Address, storageLeafKey common.Hash, number uint64) (hexutil.Bytes, error) {
|
||||
hash := b.GetCanonicalHash(number)
|
||||
if hash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
|
||||
func (b *Backend) GetStorageByNumber(ctx context.Context, address common.Address, key common.Hash, blockNumber rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||
var err error
|
||||
number := blockNumber.Int64()
|
||||
if blockNumber == rpc.LatestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveLastBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b.GetStorageByHash(ctx, address, storageLeafKey, hash)
|
||||
if blockNumber == rpc.EarliestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveFirstBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.PendingBlockNumber {
|
||||
return nil, errPendingBlockNumber
|
||||
}
|
||||
hash, err := b.GetCanonicalHash(uint64(number))
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errHeaderNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.GetStorageByHash(ctx, address, key, hash)
|
||||
}
|
||||
|
||||
// GetStorageByHash returns the storage value for the provided contract address an storage key at the block corresponding to the provided hash
|
||||
func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address, storageLeafKey, hash common.Hash) (hexutil.Bytes, error) {
|
||||
_, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(address, storageLeafKey, hash)
|
||||
func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address, key, hash common.Hash) (hexutil.Bytes, error) {
|
||||
_, err := b.HeaderByHash(context.Background(), hash)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errHeaderHashNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash)
|
||||
return storageRlp, err
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,12 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
||||
)
|
||||
@ -244,23 +244,91 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransacti
|
||||
}
|
||||
|
||||
// extractLogsOfInterest returns logs from the receipt IPLD
|
||||
func extractLogsOfInterest(rctIPLDs []ipfs.BlockModel, wantedTopics [][]string) ([]*types.Log, error) {
|
||||
var logs []*types.Log
|
||||
for _, rctIPLD := range rctIPLDs {
|
||||
rctRLP := rctIPLD
|
||||
var rct types.Receipt
|
||||
if err := rlp.DecodeBytes(rctRLP.Data, &rct); err != nil {
|
||||
func extractLogsOfInterest(config *params.ChainConfig, blockHash common.Hash, blockNumber uint64,
|
||||
txs types.Transactions, rctIPLDs []ipfs.BlockModel, filter ReceiptFilter) ([]*types.Log, error) {
|
||||
receipts := make(types.Receipts, len(rctIPLDs))
|
||||
|
||||
for i, rctBytes := range rctIPLDs {
|
||||
rct := new(types.Receipt)
|
||||
if err := rlp.DecodeBytes(rctBytes.Data, rct); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, log := range rct.Logs {
|
||||
if wanted := wantedLog(wantedTopics, log.Topics); wanted == true {
|
||||
logs = append(logs, log)
|
||||
}
|
||||
receipts[i] = rct
|
||||
}
|
||||
|
||||
err := receipts.DeriveFields(config, blockHash, blockNumber, txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var unfilteredLogs []*types.Log
|
||||
for _, receipt := range receipts {
|
||||
unfilteredLogs = append(unfilteredLogs, receipt.Logs...)
|
||||
}
|
||||
|
||||
adders := make([]common.Address, len(filter.LogAddresses))
|
||||
for i, addr := range filter.LogAddresses {
|
||||
adders[i] = common.HexToAddress(addr)
|
||||
}
|
||||
|
||||
topics := make([][]common.Hash, len(filter.Topics))
|
||||
for i, v := range filter.Topics {
|
||||
topics[i] = make([]common.Hash, len(v))
|
||||
for j, topic := range v {
|
||||
topics[i][j] = common.HexToHash(topic)
|
||||
}
|
||||
}
|
||||
|
||||
logs := filterLogs(unfilteredLogs, nil, nil, adders, topics)
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func includes(addresses []common.Address, a common.Address) bool {
|
||||
for _, addr := range addresses {
|
||||
if addr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// filterLogs creates a slice of logs matching the given criteria.
|
||||
func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
|
||||
var ret []*types.Log
|
||||
Logs:
|
||||
for _, log := range logs {
|
||||
if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(addresses) > 0 && !includes(addresses, log.Address) {
|
||||
continue
|
||||
}
|
||||
// If the to filtered topics is greater than the amount of topics in logs, skip.
|
||||
if len(topics) > len(log.Topics) {
|
||||
continue
|
||||
}
|
||||
for i, sub := range topics {
|
||||
match := len(sub) == 0 // empty rule set == wildcard
|
||||
for _, topic := range sub {
|
||||
if log.Topics[i] == topic {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
continue Logs
|
||||
}
|
||||
}
|
||||
ret = append(ret, log)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// returns true if the log matches on the filter
|
||||
func wantedLog(wantedTopics [][]string, actualTopics []common.Hash) bool {
|
||||
// actualTopics will always have length <= 4
|
||||
|
@ -312,6 +312,14 @@ func (ecr *CIDRetriever) RetrieveRctCIDs(tx *sqlx.Tx, rctFilter ReceiptFilter, b
|
||||
args = append(args, blockHash.String())
|
||||
id++
|
||||
}
|
||||
|
||||
// TODO: Add the below filters when we have log index in DB.
|
||||
if true {
|
||||
pgStr += ` ORDER BY transaction_cids.index`
|
||||
receiptCids := make([]eth.ReceiptModel, 0)
|
||||
return receiptCids, tx.Select(&receiptCids, pgStr, args...)
|
||||
}
|
||||
|
||||
if len(rctFilter.LogAddresses) > 0 {
|
||||
// Filter on log contract addresses if there are any
|
||||
pgStr += fmt.Sprintf(` AND ((receipt_cids.log_contracts && $%d::VARCHAR(66)[]`, id)
|
||||
@ -373,6 +381,7 @@ func (ecr *CIDRetriever) RetrieveRctCIDs(tx *sqlx.Tx, rctFilter ReceiptFilter, b
|
||||
args = append(args, pq.Array(trxIds))
|
||||
}
|
||||
}
|
||||
|
||||
pgStr += ` ORDER BY transaction_cids.index`
|
||||
receiptCids := make([]eth.ReceiptModel, 0)
|
||||
return receiptCids, tx.Select(&receiptCids, pgStr, args...)
|
||||
|
@ -237,6 +237,7 @@ var _ = Describe("Retriever", func() {
|
||||
Expect(empty).ToNot(BeTrue())
|
||||
Expect(len(cids)).To(Equal(1))
|
||||
Expect(cids[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
||||
|
||||
expectedHeaderCID := test_helpers.MockCIDWrapper.Header
|
||||
expectedHeaderCID.ID = cids[0].Header.ID
|
||||
expectedHeaderCID.NodeID = cids[0].Header.NodeID
|
||||
@ -250,6 +251,7 @@ var _ = Describe("Retriever", func() {
|
||||
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, test_helpers.MockCIDWrapper.Receipts[1].CID)).To(BeTrue())
|
||||
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, test_helpers.MockCIDWrapper.Receipts[2].CID)).To(BeTrue())
|
||||
Expect(len(cids[0].StateNodes)).To(Equal(2))
|
||||
|
||||
for _, stateNode := range cids[0].StateNodes {
|
||||
if stateNode.CID == test_helpers.State1CID.String() {
|
||||
Expect(stateNode.StateKey).To(Equal(common.BytesToHash(test_helpers.ContractLeafKey).Hex()))
|
||||
|
@ -38,7 +38,6 @@ import (
|
||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/shared"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||
)
|
||||
@ -60,6 +59,7 @@ func init() {
|
||||
}
|
||||
|
||||
var _ = Describe("eth state reading tests", func() {
|
||||
const chainLength = 5
|
||||
var (
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
@ -80,13 +80,13 @@ var _ = Describe("eth state reading tests", func() {
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VmConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000),
|
||||
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api = eth.NewPublicEthAPI(backend, nil, false)
|
||||
|
||||
// make the test blockchain (and state)
|
||||
blocks, receipts, chain = test_helpers.MakeChain(5, test_helpers.Genesis, test_helpers.TestChainGen)
|
||||
blocks, receipts, chain = test_helpers.MakeChain(chainLength, test_helpers.Genesis, test_helpers.TestChainGen)
|
||||
params := statediff.Params{
|
||||
IntermediateStateNodes: true,
|
||||
IntermediateStorageNodes: true,
|
||||
@ -234,12 +234,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock1))
|
||||
@ -247,12 +250,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
@ -260,12 +266,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
@ -273,12 +282,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
@ -286,12 +298,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
@ -304,12 +319,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock1))
|
||||
@ -317,12 +335,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
@ -330,12 +351,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
@ -343,12 +367,15 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
@ -356,37 +383,45 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the balance for an account at the provided block number", func() {
|
||||
_, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns `0` for an account it cannot find the balance for an account at the provided block number", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the balance for an account at the provided block hash", func() {
|
||||
_, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns `0` for an error for an account it cannot find the balance for an account at the provided block hash", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
@ -409,52 +444,64 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the code for", func() {
|
||||
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
It("Returns `nil` for an account it cannot find the code for", func() {
|
||||
code, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(BeEmpty())
|
||||
})
|
||||
It("Throws an error for a contract that doesn't exist at this hieght", func() {
|
||||
_, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
It("Returns `nil` for a contract that doesn't exist at this height", func() {
|
||||
code, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getStorageAt", func() {
|
||||
It("Throws an error if it tries to access a contract which does not exist", func() {
|
||||
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns empty slice if it tries to access a contract which does not exist", func() {
|
||||
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
|
||||
|
||||
_, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
storage, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
|
||||
})
|
||||
It("Throws an error if it tries to access a contract slot which does not exist", func() {
|
||||
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
It("Returns empty slice if it tries to access a contract slot which does not exist", func() {
|
||||
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
|
||||
})
|
||||
It("Retrieves the storage value at the provided contract address and storage leaf key at the block with the provided hash or number", func() {
|
||||
// After deployment
|
||||
val, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
|
||||
val, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes := hexutil.Bytes(common.Hex2Bytes("01"))
|
||||
expectedRes := hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||
Expect(val).To(Equal(expectedRes))
|
||||
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(3))
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("03"))
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
||||
Expect(val).To(Equal(expectedRes))
|
||||
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(4))
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("09"))
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000009"))
|
||||
Expect(val).To(Equal(expectedRes))
|
||||
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(5))
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(val).To(Equal(hexutil.Bytes{}))
|
||||
})
|
||||
It("Throws an error for a non-existing block hash", func() {
|
||||
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithHash(randomHash, true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError("header for hash not found"))
|
||||
})
|
||||
It("Throws an error for a non-existing block number", func() {
|
||||
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(chainLength+1))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError("header not found"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getHeaderByNumber", func() {
|
||||
|
@ -428,11 +428,12 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Ad
|
||||
return accountResult.CID, i[1].([]byte), nil
|
||||
}
|
||||
|
||||
// RetrieveStorageAtByAddressAndStorageKeyAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage key, and block hash
|
||||
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(address common.Address, storageLeafKey, hash common.Hash) (string, []byte, error) {
|
||||
// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash
|
||||
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, error) {
|
||||
storageResult := new(nodeInfo)
|
||||
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageLeafKey.Hex(), hash.Hex()); err != nil {
|
||||
storageHash := crypto.Keccak256Hash(key.Bytes())
|
||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if storageResult.Removed {
|
||||
|
@ -34,7 +34,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/statediff/testhelpers"
|
||||
"github.com/ipfs/go-block-format"
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
"github.com/multiformats/go-multihash"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
@ -80,7 +80,7 @@ var (
|
||||
},
|
||||
}
|
||||
ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts)
|
||||
MockBlock = types.NewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
|
||||
MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
|
||||
MockHeaderRlp, _ = rlp.EncodeToBytes(MockBlock.Header())
|
||||
MockChildHeader = types.Header{
|
||||
Time: 0,
|
||||
@ -104,15 +104,22 @@ var (
|
||||
mockTopic21 = common.HexToHash("0x05")
|
||||
mockTopic22 = common.HexToHash("0x07")
|
||||
MockLog1 = &types.Log{
|
||||
Address: Address,
|
||||
Topics: []common.Hash{mockTopic11, mockTopic12},
|
||||
Data: []byte{},
|
||||
Address: Address,
|
||||
Topics: []common.Hash{mockTopic11, mockTopic12},
|
||||
Data: []byte{},
|
||||
BlockNumber: BlockNumber.Uint64(),
|
||||
TxIndex: 0,
|
||||
Index: 0,
|
||||
}
|
||||
MockLog2 = &types.Log{
|
||||
Address: AnotherAddress,
|
||||
Topics: []common.Hash{mockTopic21, mockTopic22},
|
||||
Data: []byte{},
|
||||
Address: AnotherAddress,
|
||||
Topics: []common.Hash{mockTopic21, mockTopic22},
|
||||
Data: []byte{},
|
||||
BlockNumber: BlockNumber.Uint64(),
|
||||
TxIndex: 1,
|
||||
Index: 1,
|
||||
}
|
||||
|
||||
HeaderCID, _ = ipld.RawdataToCid(ipld.MEthHeader, MockHeaderRlp, multihash.KECCAK_256)
|
||||
HeaderMhKey = shared.MultihashKeyFromCID(HeaderCID)
|
||||
Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, MockTransactions.GetRlp(0), multihash.KECCAK_256)
|
||||
@ -375,6 +382,8 @@ var (
|
||||
StateNodes: MockStateNodes,
|
||||
}
|
||||
|
||||
Reward = eth.CalcEthBlockReward(MockBlock.Header(), MockBlock.Uncles(), MockBlock.Transactions(), MockReceipts)
|
||||
|
||||
MockCIDWrapper = ð2.CIDWrapper{
|
||||
BlockNumber: new(big.Int).Set(BlockNumber),
|
||||
Header: eth.HeaderModel{
|
||||
@ -384,7 +393,7 @@ var (
|
||||
CID: HeaderCID.String(),
|
||||
MhKey: HeaderMhKey,
|
||||
TotalDifficulty: MockBlock.Difficulty().String(),
|
||||
Reward: "5312500000000000000",
|
||||
Reward: Reward.String(),
|
||||
StateRoot: MockBlock.Root().String(),
|
||||
RctRoot: MockBlock.ReceiptHash().String(),
|
||||
TxRoot: MockBlock.TxHash().String(),
|
||||
@ -489,6 +498,17 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func createNewBlock(header *types.Header, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, hasher types.Hasher) *types.Block {
|
||||
block := types.NewBlock(header, txs, uncles, receipts, hasher)
|
||||
bHash := block.Hash()
|
||||
for _, r := range receipts {
|
||||
for _, l := range r.Logs {
|
||||
l.BlockHash = bHash
|
||||
}
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
// createTransactionsAndReceipts is a helper function to generate signed mock transactions and mock receipts with mock logs
|
||||
func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common.Address) {
|
||||
// make transactions
|
||||
@ -519,13 +539,26 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
||||
}
|
||||
// make receipts
|
||||
mockReceipt1 := types.NewReceipt(common.HexToHash("0x0").Bytes(), false, 50)
|
||||
|
||||
hash1 := signedTrx1.Hash()
|
||||
MockLog1.TxHash = hash1
|
||||
|
||||
mockReceipt1.Logs = []*types.Log{MockLog1}
|
||||
mockReceipt1.TxHash = signedTrx1.Hash()
|
||||
mockReceipt1.TxHash = hash1
|
||||
mockReceipt1.GasUsed = mockReceipt1.CumulativeGasUsed
|
||||
|
||||
mockReceipt2 := types.NewReceipt(common.HexToHash("0x1").Bytes(), false, 100)
|
||||
hash2 := signedTrx2.Hash()
|
||||
MockLog2.TxHash = hash2
|
||||
|
||||
mockReceipt2.Logs = []*types.Log{MockLog2}
|
||||
mockReceipt2.TxHash = signedTrx2.Hash()
|
||||
mockReceipt3 := types.NewReceipt(common.HexToHash("0x2").Bytes(), false, 75)
|
||||
mockReceipt2.TxHash = hash2
|
||||
mockReceipt2.GasUsed = mockReceipt2.CumulativeGasUsed - mockReceipt1.CumulativeGasUsed
|
||||
|
||||
mockReceipt3 := types.NewReceipt(common.HexToHash("0x2").Bytes(), false, 175)
|
||||
mockReceipt3.Logs = []*types.Log{}
|
||||
mockReceipt3.TxHash = signedTrx3.Hash()
|
||||
mockReceipt3.GasUsed = mockReceipt3.CumulativeGasUsed - mockReceipt2.CumulativeGasUsed
|
||||
|
||||
return types.Transactions{signedTrx1, signedTrx2, signedTrx3}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3}, SenderAddr
|
||||
}
|
||||
|
@ -867,7 +867,11 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
|
||||
if args.To != nil {
|
||||
to = rpc.BlockNumber(*args.To)
|
||||
} else {
|
||||
to = rpc.BlockNumber(r.backend.CurrentBlock().Number().Int64())
|
||||
block, err := r.backend.CurrentBlock()
|
||||
if err != nil {
|
||||
return []*Block{}, nil
|
||||
}
|
||||
to = rpc.BlockNumber(block.Number().Int64())
|
||||
}
|
||||
if to < from {
|
||||
return []*Block{}, nil
|
||||
|
@ -41,7 +41,7 @@ func StartHTTPEndpoint(endpoint string, apis []rpc.API, modules []string, cors [
|
||||
utils.Fatalf("Could not start RPC api: %v", err)
|
||||
}
|
||||
extapiURL := fmt.Sprintf("http://%v/", addr)
|
||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||
log.Infof("HTTP endpoint opened %s", extapiURL)
|
||||
|
||||
return srv, err
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package serve
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
@ -54,14 +55,31 @@ const (
|
||||
|
||||
// Config struct
|
||||
type Config struct {
|
||||
DB *postgres.DB
|
||||
DBConfig postgres.Config
|
||||
WSEndpoint string
|
||||
HTTPEndpoint string
|
||||
IPCEndpoint string
|
||||
DB *postgres.DB
|
||||
DBConfig postgres.Config
|
||||
|
||||
WSEnabled bool
|
||||
WSEndpoint string
|
||||
|
||||
HTTPEnabled bool
|
||||
HTTPEndpoint string
|
||||
|
||||
IPCEnabled bool
|
||||
IPCEndpoint string
|
||||
|
||||
EthGraphqlEnabled bool
|
||||
EthGraphqlEndpoint string
|
||||
|
||||
IpldGraphqlEnabled bool
|
||||
IpldGraphqlEndpoint string
|
||||
IpldPostgraphileEndpoint string
|
||||
TracingHttpEndpoint string
|
||||
TracingPostgraphileEndpoint string
|
||||
|
||||
ChainConfig *params.ChainConfig
|
||||
DefaultSender *common.Address
|
||||
RPCGasCap *big.Int
|
||||
EthHttpEndpoint string
|
||||
Client *rpc.Client
|
||||
SupportStateDiff bool
|
||||
}
|
||||
@ -71,9 +89,6 @@ type Config struct {
|
||||
func NewConfig() (*Config, error) {
|
||||
c := new(Config)
|
||||
|
||||
viper.BindEnv("server.wsPath", SERVER_WS_PATH)
|
||||
viper.BindEnv("server.ipcPath", SERVER_IPC_PATH)
|
||||
viper.BindEnv("server.httpPath", SERVER_HTTP_PATH)
|
||||
viper.BindEnv("ethereum.httpPath", shared.ETH_HTTP_PATH)
|
||||
viper.BindEnv("ethereum.defaultSender", ETH_DEFAULT_SENDER_ADDR)
|
||||
viper.BindEnv("ethereum.rpcGasCap", ETH_RPC_GAS_CAP)
|
||||
@ -83,32 +98,91 @@ func NewConfig() (*Config, error) {
|
||||
c.DBConfig.Init()
|
||||
|
||||
ethHTTP := viper.GetString("ethereum.httpPath")
|
||||
nodeInfo, cli, err := shared.GetEthNodeAndClient(fmt.Sprintf("http://%s", ethHTTP))
|
||||
ethHTTPEndpoint := fmt.Sprintf("http://%s", ethHTTP)
|
||||
nodeInfo, cli, err := shared.GetEthNodeAndClient(ethHTTPEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Client = cli
|
||||
c.SupportStateDiff = viper.GetBool("ethereum.supportsStateDiff")
|
||||
c.EthHttpEndpoint = ethHTTPEndpoint
|
||||
|
||||
wsPath := viper.GetString("server.wsPath")
|
||||
if wsPath == "" {
|
||||
wsPath = "127.0.0.1:8080"
|
||||
}
|
||||
c.WSEndpoint = wsPath
|
||||
ipcPath := viper.GetString("server.ipcPath")
|
||||
if ipcPath == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// websocket server
|
||||
wsEnabled := viper.GetBool("eth.server.ws")
|
||||
if wsEnabled {
|
||||
wsPath := viper.GetString("eth.server.wsPath")
|
||||
if wsPath == "" {
|
||||
wsPath = "127.0.0.1:8080"
|
||||
}
|
||||
ipcPath = filepath.Join(home, ".vulcanize/vulcanize.ipc")
|
||||
c.WSEndpoint = wsPath
|
||||
}
|
||||
c.IPCEndpoint = ipcPath
|
||||
httpPath := viper.GetString("server.httpPath")
|
||||
if httpPath == "" {
|
||||
httpPath = "127.0.0.1:8081"
|
||||
c.WSEnabled = wsEnabled
|
||||
|
||||
// ipc server
|
||||
ipcEnabled := viper.GetBool("eth.server.ipc")
|
||||
if ipcEnabled {
|
||||
ipcPath := viper.GetString("eth.server.ipcPath")
|
||||
if ipcPath == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipcPath = filepath.Join(home, ".vulcanize/vulcanize.ipc")
|
||||
}
|
||||
c.IPCEndpoint = ipcPath
|
||||
}
|
||||
c.HTTPEndpoint = httpPath
|
||||
c.IPCEnabled = ipcEnabled
|
||||
|
||||
// http server
|
||||
httpEnabled := viper.GetBool("eth.server.http")
|
||||
if httpEnabled {
|
||||
httpPath := viper.GetString("eth.server.httpPath")
|
||||
if httpPath == "" {
|
||||
httpPath = "127.0.0.1:8081"
|
||||
}
|
||||
c.HTTPEndpoint = httpPath
|
||||
}
|
||||
c.HTTPEnabled = httpEnabled
|
||||
|
||||
// eth graphql endpoint
|
||||
ethGraphqlEnabled := viper.GetBool("eth.server.graphql")
|
||||
if ethGraphqlEnabled {
|
||||
ethGraphqlPath := viper.GetString("eth.server.graphqlPath")
|
||||
if ethGraphqlPath == "" {
|
||||
ethGraphqlPath = "127.0.0.1:8082"
|
||||
}
|
||||
c.EthGraphqlEndpoint = ethGraphqlPath
|
||||
}
|
||||
c.EthGraphqlEnabled = ethGraphqlEnabled
|
||||
|
||||
// ipld graphql endpoint
|
||||
ipldGraphqlEnabled := viper.GetBool("ipld.server.graphql")
|
||||
if ipldGraphqlEnabled {
|
||||
ipldGraphqlPath := viper.GetString("ipld.server.graphqlPath")
|
||||
if ipldGraphqlPath == "" {
|
||||
ipldGraphqlPath = "127.0.0.1:8083"
|
||||
}
|
||||
c.IpldGraphqlEndpoint = ipldGraphqlPath
|
||||
|
||||
ipldPostgraphilePath := viper.GetString("ipld.postgraphilePath")
|
||||
if ipldPostgraphilePath == "" {
|
||||
return nil, errors.New("ipld-postgraphile-path parameter is empty")
|
||||
}
|
||||
c.IpldPostgraphileEndpoint = ipldPostgraphilePath
|
||||
|
||||
tracingHttpEndpoint := viper.GetString("tracing.httpPath")
|
||||
tracingPostgraphilePath := viper.GetString("tracing.postgraphilePath")
|
||||
|
||||
// these two parameters either can be both empty or both set
|
||||
if (tracingHttpEndpoint == "" && tracingPostgraphilePath != "") || (tracingHttpEndpoint != "" && tracingPostgraphilePath == "") {
|
||||
return nil, errors.New("tracing.httpPath and tracing.postgraphilePath parameters either can be both empty or both set")
|
||||
}
|
||||
|
||||
c.TracingHttpEndpoint = tracingHttpEndpoint
|
||||
c.TracingPostgraphileEndpoint = tracingPostgraphilePath
|
||||
}
|
||||
c.IpldGraphqlEnabled = ipldGraphqlEnabled
|
||||
|
||||
overrideDBConnConfig(&c.DBConfig)
|
||||
serveDB := utils.LoadPostgres(c.DBConfig, nodeInfo, false)
|
||||
prom.RegisterDBCollector(c.DBConfig.Name, serveDB.DB)
|
||||
|
21
scripts/run_intregration_test.sh
Executable file
21
scripts/run_intregration_test.sh
Executable file
@ -0,0 +1,21 @@
|
||||
set -e
|
||||
set -o xtrace
|
||||
|
||||
# Clear up existing docker images and volume.
|
||||
docker-compose down --remove-orphans --volumes
|
||||
|
||||
# Build and start the containers.
|
||||
# Note: Build only if `ipld-eth-server` code is modified. Otherwise comment this line.
|
||||
docker build -t ipld-eth-server_eth-server:latest .
|
||||
docker-compose -f docker-compose.test.yml -f docker-compose.yml up -d db dapptools contract eth-server
|
||||
|
||||
export PGPASSWORD=password
|
||||
export DATABASE_USER=vdbm
|
||||
export DATABASE_PORT=8077
|
||||
export DATABASE_PASSWORD=password
|
||||
export DATABASE_HOSTNAME=127.0.0.1
|
||||
|
||||
# Wait for containers to be up and execute the integration test.
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8081)" != "200" ]; do echo "waiting for ipld-eth-server..." && sleep 5; done && \
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8545)" != "200" ]; do echo "waiting for geth-statediff..." && sleep 5; done && \
|
||||
make integrationtest
|
2
scripts/run_unit_test.sh
Executable file
2
scripts/run_unit_test.sh
Executable file
@ -0,0 +1,2 @@
|
||||
docker-compose -f docker-compose.test.yml -f docker-compose.yml up -d db
|
||||
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=127.0.0.1 make test
|
15
test/README.md
Normal file
15
test/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
Spin up services:
|
||||
```
|
||||
docker-compose -f docker-compose.test.yml -f docker-compose.yml up -d db dapptools contract eth-server
|
||||
```
|
||||
|
||||
Running unit tests:
|
||||
```bash
|
||||
make test_local
|
||||
```
|
||||
|
||||
Running integration test:
|
||||
```bash
|
||||
make integrationtest_local
|
||||
```
|
3
test/contract/.dockerignore
Normal file
3
test/contract/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
artifacts
|
||||
cache
|
5
test/contract/.gitignore
vendored
Normal file
5
test/contract/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
|
||||
#Hardhat files
|
||||
cache
|
||||
artifacts
|
14
test/contract/Dockerfile
Normal file
14
test/contract/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
FROM node:14
|
||||
|
||||
ARG ETH_ADDR
|
||||
ENV ETH_ADDR $ETH_ADDR
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run compile && ls -lah
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENTRYPOINT ["npm", "start"]
|
7
test/contract/contracts/GLDToken.sol
Normal file
7
test/contract/contracts/GLDToken.sol
Normal file
@ -0,0 +1,7 @@
|
||||
pragma solidity ^0.8.0;
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
contract GLDToken is ERC20 {
|
||||
constructor() ERC20("Gold", "GLD") {
|
||||
_mint(msg.sender, 1000000000000000000000);
|
||||
}
|
||||
}
|
32
test/contract/hardhat.config.js
Normal file
32
test/contract/hardhat.config.js
Normal file
@ -0,0 +1,32 @@
|
||||
require("@nomiclabs/hardhat-waffle");
|
||||
|
||||
// This is a sample Hardhat task. To learn how to create your own go to
|
||||
// https://hardhat.org/guides/create-task.html
|
||||
task("accounts", "Prints the list of accounts", async () => {
|
||||
const accounts = await ethers.getSigners();
|
||||
|
||||
for (const account of accounts) {
|
||||
console.log(account.address);
|
||||
}
|
||||
});
|
||||
|
||||
// You need to export an object to set up your config
|
||||
// Go to https://hardhat.org/config/ to learn more
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
module.exports = {
|
||||
solidity: "0.8.0",
|
||||
networks: {
|
||||
local: {
|
||||
url: 'http://127.0.0.1:8545',
|
||||
chainId: 4
|
||||
},
|
||||
docker: {
|
||||
url: process.env.ETH_ADDR,
|
||||
chainId: 4
|
||||
}
|
||||
}
|
||||
};
|
||||
|
27700
test/contract/package-lock.json
generated
Normal file
27700
test/contract/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
test/contract/package.json
Normal file
27
test/contract/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "contract",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"compile": "npx hardhat compile",
|
||||
"start": "HARDHAT_NETWORK=docker node src/index.js",
|
||||
"start:local": "ETH_ADDR=http://127.0.0.1:8545 npm run start",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^4.0.0",
|
||||
"fastify": "^3.14.2",
|
||||
"hardhat": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"chai": "^4.3.4",
|
||||
"ethereum-waffle": "^3.3.0",
|
||||
"ethers": "^5.1.0"
|
||||
}
|
||||
}
|
18
test/contract/scripts/deploy.js
Normal file
18
test/contract/scripts/deploy.js
Normal file
@ -0,0 +1,18 @@
|
||||
const hre = require("hardhat");
|
||||
|
||||
async function main() {
|
||||
// await hre.run('compile');
|
||||
// We get the contract to deploy
|
||||
const GLDToken = await hre.ethers.getContractFactory("GLDToken");
|
||||
const token = await GLDToken.deploy();
|
||||
await token.deployed();
|
||||
console.log("GLDToken deployed to:", token.address, token.deployTransaction.hash);
|
||||
}
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
36
test/contract/scripts/sample-script.js
Normal file
36
test/contract/scripts/sample-script.js
Normal file
@ -0,0 +1,36 @@
|
||||
// We require the Hardhat Runtime Environment explicitly here. This is optional
|
||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
||||
//
|
||||
// When running the script with `hardhat run <script>` you'll find the Hardhat
|
||||
// Runtime Environment's members available in the global scope.
|
||||
const hre = require("hardhat");
|
||||
|
||||
async function main() {
|
||||
// Hardhat always runs the compile task when running scripts with its command
|
||||
// line interface.
|
||||
//
|
||||
// If this script is run directly using `node` you may want to call compile
|
||||
// manually to make sure everything is compiled
|
||||
// await hre.run('compile');
|
||||
|
||||
// We get the contract to deploy
|
||||
const Greeter = await hre.ethers.getContractFactory("Greeter");
|
||||
const greeter = await Greeter.deploy("Hello, Hardhat!");
|
||||
|
||||
await greeter.deployed();
|
||||
|
||||
console.log("Greeter deployed to:", greeter.address, "; tx hash: ", greeter.deployTransaction.hash);
|
||||
|
||||
const result = await greeter.setGreeting("Hello 123!");
|
||||
|
||||
console.log("Greeter updated", "; tx hash: ", result.hash, "; block hash: ", result.blockHash);
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
60
test/contract/src/index.js
Normal file
60
test/contract/src/index.js
Normal file
@ -0,0 +1,60 @@
|
||||
const fastify = require('fastify')({ logger: true });
|
||||
const hre = require("hardhat");
|
||||
|
||||
// readiness check
|
||||
fastify.get('/v1/healthz', async (req, reply) => {
|
||||
reply
|
||||
.code(200)
|
||||
.header('Content-Type', 'application/json; charset=utf-8')
|
||||
.send({ success: true })
|
||||
});
|
||||
|
||||
fastify.get('/v1/deployContract', async (req, reply) => {
|
||||
const GLDToken = await hre.ethers.getContractFactory("GLDToken");
|
||||
const token = await GLDToken.deploy();
|
||||
await token.deployed();
|
||||
|
||||
return {
|
||||
address: token.address,
|
||||
txHash: token.deployTransaction.hash,
|
||||
blockNumber: token.deployTransaction.blockNumber,
|
||||
blockHash: token.deployTransaction.blockHash,
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/v1/sendEth', async (req, reply) => {
|
||||
const to = req.query.to;
|
||||
const value = req.query.value;
|
||||
|
||||
const [owner] = await hre.ethers.getSigners();
|
||||
const tx = await owner.sendTransaction({
|
||||
to,
|
||||
value: hre.ethers.utils.parseEther(value)
|
||||
});
|
||||
await tx.wait(1)
|
||||
|
||||
// console.log(tx);
|
||||
// const coinbaseBalance = await hre.ethers.provider.getBalance(owner.address);
|
||||
// const receiverBalance = await hre.ethers.provider.getBalance(to);
|
||||
// console.log(coinbaseBalance.toString(), receiverBalance.toString());
|
||||
|
||||
return {
|
||||
from: tx.from,
|
||||
to: tx.to,
|
||||
//value: tx.value.toString(),
|
||||
txHash: tx.hash,
|
||||
blockNumber: tx.blockNumber,
|
||||
blockHash: tx.blockHash,
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await fastify.listen(3000, '0.0.0.0');
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
14
test/contract/test/sample-test.js
Normal file
14
test/contract/test/sample-test.js
Normal file
@ -0,0 +1,14 @@
|
||||
const { expect } = require("chai");
|
||||
|
||||
describe("Greeter", function() {
|
||||
it("Should return the new greeting once it's changed", async function() {
|
||||
const Greeter = await ethers.getContractFactory("Greeter");
|
||||
const greeter = await Greeter.deploy("Hello, world!");
|
||||
|
||||
await greeter.deployed();
|
||||
expect(await greeter.greet()).to.equal("Hello, world!");
|
||||
|
||||
await greeter.setGreeting("Hola, mundo!");
|
||||
expect(await greeter.greet()).to.equal("Hola, mundo!");
|
||||
});
|
||||
});
|
62
test/helper.go
Normal file
62
test/helper.go
Normal file
@ -0,0 +1,62 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ContractDeployed struct {
|
||||
Address string `json:"address"`
|
||||
TransactionHash string `json:"txHash"`
|
||||
BlockNumber int `json:"blockNumber"`
|
||||
BlockHash string `json:"blockHash"`
|
||||
}
|
||||
|
||||
type Tx struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Value *big.Int `json:"value"`
|
||||
TransactionHash string `json:"txHash"`
|
||||
BlockNumber int `json:"blockNumber"`
|
||||
BlockHash string `json:"blockHash"`
|
||||
}
|
||||
|
||||
const srvUrl = "http://localhost:3000"
|
||||
|
||||
func DeployContract() (*ContractDeployed, error) {
|
||||
res, err := http.Get(fmt.Sprintf("%s/v1/deployContract", srvUrl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var contract ContractDeployed
|
||||
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
err = decoder.Decode(&contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &contract, nil
|
||||
}
|
||||
|
||||
func SendEth(to string, value string) (*Tx, error) {
|
||||
res, err := http.Get(fmt.Sprintf("%s/v1/sendEth?to=%s&value=%s", srvUrl, to, value))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var tx Tx
|
||||
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
err = decoder.Decode(&tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tx, nil
|
||||
}
|
19
test/integration_suite_test.go
Normal file
19
test/integration_suite_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestIntegration(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "integration test suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
})
|
486
test/integration_test.go
Normal file
486
test/integration_test.go
Normal file
@ -0,0 +1,486 @@
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
integration "github.com/vulcanize/ipld-eth-server/test"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
)
|
||||
|
||||
const nonExistingBlockHash = "0x111111111111111111111111111111111111111111111111111111111111111"
|
||||
const nonExistingAddress = "0x1111111111111111111111111111111111111111"
|
||||
|
||||
var (
|
||||
randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
|
||||
randomHash = crypto.Keccak256Hash(randomAddr.Bytes())
|
||||
)
|
||||
|
||||
var _ = Describe("Integration test", func() {
|
||||
gethHttpPath := "http://127.0.0.1:8545"
|
||||
gethClient, err := ethclient.Dial(gethHttpPath)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldEthHttpPath := "http://127.0.0.1:8081"
|
||||
ipldClient, err := ethclient.Dial(ipldEthHttpPath)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var contract *integration.ContractDeployed
|
||||
var erc20TotalSupply *big.Int
|
||||
var tx *integration.Tx
|
||||
var bigIntResult bool
|
||||
var contractErr error
|
||||
var txErr error
|
||||
sleepInterval := 2 * time.Second
|
||||
|
||||
Describe("get Block", func() {
|
||||
BeforeEach(func() {
|
||||
contract, contractErr = integration.DeployContract()
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("get not existing block by number", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
blockNum := contract.BlockNumber + 100
|
||||
|
||||
gethBlock, err := gethClient.BlockByNumber(ctx, big.NewInt(int64(blockNum)))
|
||||
Expect(err).To(MatchError(ethereum.NotFound))
|
||||
Expect(gethBlock).To(BeZero())
|
||||
|
||||
ipldBlock, err := ipldClient.BlockByNumber(ctx, big.NewInt(int64(blockNum)))
|
||||
Expect(err).To(MatchError(ethereum.NotFound))
|
||||
Expect(ipldBlock).To(BeZero())
|
||||
})
|
||||
|
||||
It("get not existing block by hash", func() {
|
||||
gethBlock, err := gethClient.BlockByHash(ctx, common.HexToHash(nonExistingBlockHash))
|
||||
Expect(err).To(MatchError(ethereum.NotFound))
|
||||
Expect(gethBlock).To(BeZero())
|
||||
|
||||
ipldBlock, err := ipldClient.BlockByHash(ctx, common.HexToHash(nonExistingBlockHash))
|
||||
Expect(err).To(MatchError(ethereum.NotFound))
|
||||
Expect(ipldBlock).To(BeZero())
|
||||
})
|
||||
|
||||
It("get block by number", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
blockNum := contract.BlockNumber
|
||||
|
||||
gethBlock, err := gethClient.BlockByNumber(ctx, big.NewInt(int64(blockNum)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldBlock, err := ipldClient.BlockByNumber(ctx, big.NewInt(int64(blockNum)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// check headers are equals
|
||||
Expect(gethBlock.Header()).To(Equal(ipldBlock.Header()))
|
||||
|
||||
gethTxs := gethBlock.Transactions()
|
||||
ipldTxs := ipldBlock.Transactions()
|
||||
|
||||
Expect(gethTxs.Len()).To(Equal(ipldTxs.Len()))
|
||||
Expect(types.TxDifference(gethTxs, ipldTxs).Len()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("get block by hash", func() {
|
||||
gethBlock, err := gethClient.BlockByHash(ctx, common.HexToHash(contract.BlockHash))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldBlock, err := ipldClient.BlockByHash(ctx, common.HexToHash(contract.BlockHash))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// check headers are equals
|
||||
compareBlocks(gethBlock, ipldBlock)
|
||||
|
||||
gethTxs := gethBlock.Transactions()
|
||||
ipldTxs := ipldBlock.Transactions()
|
||||
|
||||
Expect(gethTxs.Len()).To(Equal(ipldTxs.Len()))
|
||||
Expect(types.TxDifference(gethTxs, ipldTxs).Len()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Transaction", func() {
|
||||
BeforeEach(func() {
|
||||
contract, contractErr = integration.DeployContract()
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("Get tx by hash", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
gethTx, _, err := gethClient.TransactionByHash(ctx, common.HexToHash(contract.TransactionHash))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldTx, _, err := ipldClient.TransactionByHash(ctx, common.HexToHash(contract.TransactionHash))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
compareTxs(gethTx, ipldTx)
|
||||
|
||||
Expect(gethTx.Hash()).To(Equal(ipldTx.Hash()))
|
||||
})
|
||||
|
||||
It("Get tx by block hash and index", func() {
|
||||
gethTx, err := gethClient.TransactionInBlock(ctx, common.HexToHash(contract.BlockHash), 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldTx, err := ipldClient.TransactionInBlock(ctx, common.HexToHash(contract.BlockHash), 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
compareTxs(gethTx, ipldTx)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Receipt", func() {
|
||||
BeforeEach(func() {
|
||||
contract, contractErr = integration.DeployContract()
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("Get tx receipt", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
gethReceipt, err := gethClient.TransactionReceipt(ctx, common.HexToHash(contract.TransactionHash))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldReceipt, err := ipldClient.TransactionReceipt(ctx, common.HexToHash(contract.TransactionHash))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethReceipt).To(Equal(ipldReceipt))
|
||||
|
||||
rlpGeth, err := rlp.EncodeToBytes(gethReceipt)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
rlpIpld, err := rlp.EncodeToBytes(ipldReceipt)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(rlpGeth).To(Equal(rlpIpld))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("FilterLogs", func() {
|
||||
BeforeEach(func() {
|
||||
contract, contractErr = integration.DeployContract()
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("with blockhash", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
blockHash := common.HexToHash(contract.BlockHash)
|
||||
filterQuery := ethereum.FilterQuery{
|
||||
//Addresses: addresses,
|
||||
BlockHash: &blockHash,
|
||||
Topics: [][]common.Hash{},
|
||||
}
|
||||
|
||||
gethLogs, err := gethClient.FilterLogs(ctx, filterQuery)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldLogs, err := ipldClient.FilterLogs(ctx, filterQuery)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// not empty list
|
||||
Expect(gethLogs).ToNot(BeEmpty())
|
||||
|
||||
Expect(len(gethLogs)).To(Equal(len(ipldLogs)))
|
||||
Expect(gethLogs).To(Equal(ipldLogs))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("CodeAt", func() {
|
||||
BeforeEach(func() {
|
||||
contract, contractErr = integration.DeployContract()
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("gets code at non-existing address without block number", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
gethCode, err := gethClient.CodeAt(ctx, common.HexToAddress(nonExistingAddress), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldCode, err := ipldClient.CodeAt(ctx, common.HexToAddress(nonExistingAddress), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethCode).To(BeEmpty())
|
||||
Expect(gethCode).To(Equal(ipldCode))
|
||||
})
|
||||
It("gets code of deployed contract without block number", func() {
|
||||
gethCode, err := gethClient.CodeAt(ctx, common.HexToAddress(contract.Address), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldCode, err := ipldClient.CodeAt(ctx, common.HexToAddress(contract.Address), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(gethCode).To(Equal(ipldCode))
|
||||
})
|
||||
It("gets code of deployed contract with block number", func() {
|
||||
gethCode, err := gethClient.CodeAt(ctx, common.HexToAddress(contract.Address), big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldCode, err := ipldClient.CodeAt(ctx, common.HexToAddress(contract.Address), big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(gethCode).To(Equal(ipldCode))
|
||||
})
|
||||
It("gets code of contract that doesn't exist at this height", func() {
|
||||
gethCode, err := gethClient.CodeAt(ctx, common.HexToAddress(contract.Address), big.NewInt(int64(contract.BlockNumber-1)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldCode, err := ipldClient.CodeAt(ctx, common.HexToAddress(contract.Address), big.NewInt(int64(contract.BlockNumber-1)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethCode).To(BeEmpty())
|
||||
Expect(gethCode).To(Equal(ipldCode))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Get balance", func() {
|
||||
address := "0x1111111111111111111111111111111111111112"
|
||||
BeforeEach(func() {
|
||||
tx, txErr = integration.SendEth(address, "0.01")
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("gets balance for an account with eth without block number", func() {
|
||||
Expect(txErr).ToNot(HaveOccurred())
|
||||
|
||||
gethBalance, err := gethClient.BalanceAt(ctx, common.HexToAddress(address), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldBalance, err := ipldClient.BalanceAt(ctx, common.HexToAddress(address), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethBalance).To(Equal(ipldBalance))
|
||||
})
|
||||
It("gets balance for an account with eth with block number", func() {
|
||||
Expect(txErr).ToNot(HaveOccurred())
|
||||
|
||||
gethBalance, err := gethClient.BalanceAt(ctx, common.HexToAddress(address), big.NewInt(int64(tx.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldBalance, err := ipldClient.BalanceAt(ctx, common.HexToAddress(address), big.NewInt(int64(tx.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethBalance).To(Equal(ipldBalance))
|
||||
})
|
||||
It("gets historical balance for an account with eth with block number", func() {
|
||||
Expect(txErr).ToNot(HaveOccurred())
|
||||
|
||||
gethBalance, err := gethClient.BalanceAt(ctx, common.HexToAddress(address), big.NewInt(int64(tx.BlockNumber-1)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldBalance, err := ipldClient.BalanceAt(ctx, common.HexToAddress(address), big.NewInt(int64(tx.BlockNumber-1)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethBalance).To(Equal(ipldBalance))
|
||||
})
|
||||
It("gets balance for a non-existing account without block number", func() {
|
||||
Expect(txErr).ToNot(HaveOccurred())
|
||||
|
||||
gethBalance, err := gethClient.BalanceAt(ctx, common.HexToAddress(nonExistingAddress), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldBalance, err := ipldClient.BalanceAt(ctx, common.HexToAddress(nonExistingAddress), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethBalance).To(Equal(ipldBalance))
|
||||
})
|
||||
It("gets balance for an non-existing block number", func() {
|
||||
Expect(txErr).ToNot(HaveOccurred())
|
||||
|
||||
gethBalance, err := gethClient.BalanceAt(ctx, common.HexToAddress(address), big.NewInt(int64(tx.BlockNumber+3)))
|
||||
Expect(err).To(MatchError("header not found"))
|
||||
|
||||
ipldBalance, err := ipldClient.BalanceAt(ctx, common.HexToAddress(nonExistingAddress), big.NewInt(int64(tx.BlockNumber+3)))
|
||||
Expect(err).To(MatchError("header not found"))
|
||||
|
||||
Expect(gethBalance).To(Equal(ipldBalance))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Get Storage", func() {
|
||||
BeforeEach(func() {
|
||||
contract, contractErr = integration.DeployContract()
|
||||
erc20TotalSupply, bigIntResult = new(big.Int).SetString("1000000000000000000000", 10)
|
||||
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("gets ERC20 total supply (without block number)", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
Expect(bigIntResult).To(Equal(true))
|
||||
|
||||
totalSupplyIndex := "0x2"
|
||||
|
||||
gethStorage, err := gethClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
gethTotalSupply := new(big.Int).SetBytes(gethStorage)
|
||||
Expect(gethTotalSupply).To(Equal(erc20TotalSupply))
|
||||
|
||||
ipldStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldTotalSupply := new(big.Int).SetBytes(ipldStorage)
|
||||
Expect(ipldTotalSupply).To(Equal(erc20TotalSupply))
|
||||
|
||||
Expect(gethStorage).To(Equal(ipldStorage))
|
||||
})
|
||||
|
||||
It("gets ERC20 total supply (with block number)", func() {
|
||||
totalSupplyIndex := "0x2"
|
||||
|
||||
gethStorage, err := gethClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
gethTotalSupply := new(big.Int).SetBytes(gethStorage)
|
||||
Expect(gethTotalSupply).To(Equal(erc20TotalSupply))
|
||||
|
||||
ipldStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(gethStorage).To(Equal(ipldStorage))
|
||||
})
|
||||
It("gets storage for non-existing account", func() {
|
||||
totalSupplyIndex := "0x2"
|
||||
|
||||
gethStorage, err := gethClient.StorageAt(ctx, common.HexToAddress(nonExistingAddress), common.HexToHash(totalSupplyIndex), big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(nonExistingAddress), common.HexToHash(totalSupplyIndex), big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(gethStorage).To(Equal(ipldStorage))
|
||||
})
|
||||
It("gets storage for non-existing contract slot", func() {
|
||||
gethStorage, err := gethClient.StorageAt(ctx, common.HexToAddress(contract.Address), randomHash, big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), randomHash, big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(gethStorage).To(Equal(ipldStorage))
|
||||
})
|
||||
It("gets storage for non-existing contract", func() {
|
||||
totalSupplyIndex := "0x2"
|
||||
gethStorage, err := gethClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(gethStorage).To(Equal(ipldStorage))
|
||||
})
|
||||
It("gets storage for non-existing block number", func() {
|
||||
blockNum := contract.BlockNumber + 100
|
||||
totalSupplyIndex := "0x2"
|
||||
|
||||
gethStorage, err := gethClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(int64(blockNum)))
|
||||
Expect(err).To(MatchError("header not found"))
|
||||
|
||||
ipldStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(int64(blockNum)))
|
||||
Expect(err).To(MatchError("header not found"))
|
||||
Expect(gethStorage).To(Equal(ipldStorage))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth call", func() {
|
||||
BeforeEach(func() {
|
||||
contract, contractErr = integration.DeployContract()
|
||||
erc20TotalSupply, bigIntResult = new(big.Int).SetString("1000000000000000000000", 10)
|
||||
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("calls totalSupply() without block number", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
Expect(bigIntResult).To(Equal(true))
|
||||
|
||||
contractAddress := common.HexToAddress(contract.Address)
|
||||
|
||||
msg := ethereum.CallMsg{
|
||||
To: &contractAddress,
|
||||
Data: common.Hex2Bytes("18160ddd"), // totalSupply()
|
||||
}
|
||||
gethResult, err := gethClient.CallContract(ctx, msg, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
gethTotalSupply := new(big.Int).SetBytes(gethResult)
|
||||
Expect(gethTotalSupply).To(Equal(erc20TotalSupply))
|
||||
|
||||
ipldResult, err := ipldClient.CallContract(ctx, msg, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethResult).To(Equal(ipldResult))
|
||||
})
|
||||
|
||||
It("calls totalSupply() with block number", func() {
|
||||
contractAddress := common.HexToAddress(contract.Address)
|
||||
|
||||
msg := ethereum.CallMsg{
|
||||
To: &contractAddress,
|
||||
Data: common.Hex2Bytes("18160ddd"), // totalSupply()
|
||||
}
|
||||
gethResult, err := gethClient.CallContract(ctx, msg, big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
gethTotalSupply := new(big.Int).SetBytes(gethResult)
|
||||
Expect(gethTotalSupply).To(Equal(erc20TotalSupply))
|
||||
|
||||
ipldResult, err := ipldClient.CallContract(ctx, msg, big.NewInt(int64(contract.BlockNumber)))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethResult).To(Equal(ipldResult))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Chain ID", func() {
|
||||
It("Check chain id", func() {
|
||||
gethChainId, err := gethClient.ChainID(ctx)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldChainId, err := ipldClient.ChainID(ctx)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethChainId).To(Equal(ipldChainId))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func compareBlocks(block1 *types.Block, block2 *types.Block) {
|
||||
Expect(block1.Header()).To(Equal(block2.Header()))
|
||||
Expect(block1.Uncles()).To(Equal(block2.Uncles()))
|
||||
|
||||
txs1 := block1.Transactions()
|
||||
txs2 := block2.Transactions()
|
||||
|
||||
Expect(len(txs1)).To(Equal(len(txs2)))
|
||||
for i, tx := range txs1 {
|
||||
compareTxs(tx, txs2[i])
|
||||
}
|
||||
}
|
||||
|
||||
func compareTxs(tx1 *types.Transaction, tx2 *types.Transaction) {
|
||||
Expect(tx1.Data()).To(Equal(tx2.Data()))
|
||||
Expect(tx1.Hash()).To(Equal(tx2.Hash()))
|
||||
Expect(tx1.Size()).To(Equal(tx2.Size()))
|
||||
|
||||
signer := types.NewEIP155Signer(big.NewInt(4))
|
||||
|
||||
gethSender, err := types.Sender(signer, tx1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipldSender, err := types.Sender(signer, tx2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethSender).To(Equal(ipldSender))
|
||||
}
|
Loading…
Reference in New Issue
Block a user
Very nice table, I think this helps a lot