[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
|
- name: Run docker build
|
||||||
run: make docker-build
|
run: make docker-build
|
||||||
test:
|
test:
|
||||||
name: Run integration tests
|
name: Run unit tests
|
||||||
env:
|
env:
|
||||||
GOPATH: /tmp/go
|
GOPATH: /tmp/go
|
||||||
strategy:
|
strategy:
|
||||||
@ -33,3 +33,28 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sleep 10
|
sleep 10
|
||||||
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=127.0.0.1 make test
|
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
|
# Build ipld-eth-server
|
||||||
WORKDIR /go/src/github.com/vulcanize/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
|
# Copy migration tool
|
||||||
WORKDIR /
|
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)
|
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)
|
createdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) $(TEST_DB)
|
||||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" up
|
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" up
|
||||||
$(GINKGO) -r --skipPackage=integration_tests,integration
|
$(GINKGO) -r --skipPackage=test
|
||||||
|
|
||||||
.PHONY: integrationtest
|
.PHONY: integrationtest
|
||||||
integrationtest: | $(GINKGO) $(GOOSE)
|
integrationtest: | $(GINKGO) $(GOOSE)
|
||||||
go vet ./...
|
go vet ./...
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
export PGPASSWORD=$(DATABASE_PASSWORD)
|
$(GINKGO) -r test/ -v
|
||||||
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/
|
|
||||||
|
|
||||||
.PHONY: test_local
|
.PHONY: test_local
|
||||||
test_local: | $(GINKGO) $(GOOSE)
|
test_local: | $(GINKGO) $(GOOSE)
|
||||||
go vet ./...
|
go vet ./...
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
dropdb -h $(HOST_NAME) -p $(PORT) -U $(USER) --if-exists $(TEST_DB)
|
./scripts/run_unit_test.sh
|
||||||
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
|
|
||||||
|
|
||||||
.PHONY: integrationtest_local
|
.PHONY: integrationtest_local
|
||||||
integrationtest_local: | $(GINKGO) $(GOOSE)
|
integrationtest_local: | $(GINKGO) $(GOOSE)
|
||||||
go vet ./...
|
go vet ./...
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
dropdb -h $(HOST_NAME) -p $(PORT) -U $(USER) --if-exists $(TEST_DB)
|
./scripts/run_intregration_test.sh
|
||||||
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/
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go fmt ./...
|
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)
|
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
|
### Testing
|
||||||
`make test` will run the unit tests
|
`make test` will run the unit tests
|
||||||
`make test` setups a clean `vulcanize_testing` db
|
`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()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
|
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("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-level", log.InfoLevel.String(), "log level (trace, debug, info, warn, error, fatal, panic)")
|
||||||
rootCmd.PersistentFlags().String("log-file", "", "file path for logging")
|
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-addr", "127.0.0.1", "http host for prometheus")
|
||||||
rootCmd.PersistentFlags().String("prom-http-port", "8090", "http port 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.level", rootCmd.PersistentFlags().Lookup("log-level"))
|
||||||
viper.BindPFlag("log.file", rootCmd.PersistentFlags().Lookup("log-file"))
|
viper.BindPFlag("log.file", rootCmd.PersistentFlags().Lookup("log-file"))
|
||||||
|
|
||||||
|
206
cmd/serve.go
206
cmd/serve.go
@ -16,24 +16,29 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/vulcanize/ipld-eth-server/pkg/graphql"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/vulcanize/gap-filler/pkg/mux"
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
"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"
|
srpc "github.com/vulcanize/ipld-eth-server/pkg/rpc"
|
||||||
s "github.com/vulcanize/ipld-eth-server/pkg/serve"
|
s "github.com/vulcanize/ipld-eth-server/pkg/serve"
|
||||||
v "github.com/vulcanize/ipld-eth-server/version"
|
v "github.com/vulcanize/ipld-eth-server/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrNoRpcEndpoints = errors.New("no rpc endpoints is available")
|
||||||
|
|
||||||
// serveCmd represents the serve command
|
// serveCmd represents the serve command
|
||||||
var serveCmd = &cobra.Command{
|
var serveCmd = &cobra.Command{
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
@ -71,7 +76,12 @@ func serve() {
|
|||||||
if err := startServers(server, serverConfig); err != nil {
|
if err := startServers(server, serverConfig); err != nil {
|
||||||
logWithCommand.Fatal(err)
|
logWithCommand.Fatal(err)
|
||||||
}
|
}
|
||||||
graphQL, err := startGraphQL(server)
|
graphQL, err := startEthGraphQL(server, serverConfig)
|
||||||
|
if err != nil {
|
||||||
|
logWithCommand.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = startIpldGraphQL(serverConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logWithCommand.Fatal(err)
|
logWithCommand.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -87,27 +97,43 @@ func serve() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startServers(server s.Server, settings *s.Config) error {
|
func startServers(server s.Server, settings *s.Config) error {
|
||||||
logWithCommand.Info("starting up IPC server")
|
if settings.IPCEnabled {
|
||||||
_, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
|
logWithCommand.Info("starting up IPC server")
|
||||||
if err != nil {
|
_, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
|
||||||
return err
|
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 settings.WSEnabled {
|
||||||
if err != nil {
|
logWithCommand.Info("starting up WS server")
|
||||||
return err
|
_, _, 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{})
|
if settings.HTTPEnabled {
|
||||||
return err
|
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) {
|
func startEthGraphQL(server s.Server, settings *s.Config) (graphQLServer *graphql.Service, err error) {
|
||||||
viper.BindEnv("server.graphql", "SERVER_GRAPHQL")
|
if settings.EthGraphqlEnabled {
|
||||||
if viper.GetBool("server.graphql") {
|
logWithCommand.Info("starting up ETH GraphQL server")
|
||||||
logWithCommand.Info("starting up GraphQL server")
|
endPoint := settings.EthGraphqlEndpoint
|
||||||
viper.BindEnv("server.graphqlEndpoint", "SERVER_GRAPHQL_ENDPOINT")
|
|
||||||
endPoint := viper.GetString("server.graphqlEndpoint")
|
|
||||||
if endPoint != "" {
|
if endPoint != "" {
|
||||||
graphQLServer, err = graphql.New(server.Backend(), endPoint, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
graphQLServer, err = graphql.New(server.Backend(), endPoint, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -115,19 +141,113 @@ func startGraphQL(server s.Server) (graphQLServer *graphql.Service, err error) {
|
|||||||
}
|
}
|
||||||
err = graphQLServer.Start(nil)
|
err = graphQLServer.Start(nil)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logWithCommand.Info("ETH GraphQL server is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
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() {
|
func init() {
|
||||||
rootCmd.AddCommand(serveCmd)
|
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
|
// flags for all config variables
|
||||||
serveCmd.PersistentFlags().Bool("server-graphql", false, "turn on the graphql server")
|
// eth graphql and json-rpc parameters
|
||||||
serveCmd.PersistentFlags().String("server-graphql-endpoint", "", "endpoint url for graphql server")
|
serveCmd.PersistentFlags().Bool("eth-server-graphql", false, "turn on the eth graphql server")
|
||||||
serveCmd.PersistentFlags().String("server-ws-path", "", "vdb server ws path")
|
serveCmd.PersistentFlags().String("eth-server-graphql-path", "", "endpoint url for eth graphql server (host:port)")
|
||||||
serveCmd.PersistentFlags().String("server-http-path", "", "vdb server http path")
|
serveCmd.PersistentFlags().Bool("eth-server-http", true, "turn on the eth http json-rpc server")
|
||||||
serveCmd.PersistentFlags().String("server-ipc-path", "", "vdb server ipc path")
|
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-http-path", "", "http url for ethereum node")
|
||||||
serveCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
|
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")
|
serveCmd.PersistentFlags().Bool("eth-supports-state-diff", false, "whether or not the proxy ethereum client supports statediffing endpoints")
|
||||||
|
|
||||||
// and their bindings
|
// and their bindings
|
||||||
viper.BindPFlag("server.graphql", serveCmd.PersistentFlags().Lookup("server-graphql"))
|
// database
|
||||||
viper.BindPFlag("server.graphqlEndpoint", serveCmd.PersistentFlags().Lookup("server-graphql-endpoint"))
|
viper.BindPFlag("database.name", serveCmd.PersistentFlags().Lookup("database-name"))
|
||||||
viper.BindPFlag("server.wsPath", serveCmd.PersistentFlags().Lookup("server-ws-path"))
|
viper.BindPFlag("database.port", serveCmd.PersistentFlags().Lookup("database-port"))
|
||||||
viper.BindPFlag("server.httpPath", serveCmd.PersistentFlags().Lookup("server-http-path"))
|
viper.BindPFlag("database.hostname", serveCmd.PersistentFlags().Lookup("database-hostname"))
|
||||||
viper.BindPFlag("server.ipcPath", serveCmd.PersistentFlags().Lookup("server-ipc-path"))
|
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.httpPath", serveCmd.PersistentFlags().Lookup("eth-http-path"))
|
||||||
viper.BindPFlag("ethereum.nodeID", serveCmd.PersistentFlags().Lookup("eth-node-id"))
|
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:
|
services:
|
||||||
dapptools:
|
dapptools:
|
||||||
restart: unless-stopped
|
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:
|
ports:
|
||||||
- "127.0.0.1:8545:8545"
|
- "127.0.0.1:8545:8545"
|
||||||
- "127.0.0.1:8546:8546"
|
- "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:
|
db:
|
||||||
restart: always
|
restart: always
|
||||||
image: postgres:10.12-alpine
|
image: postgres:10.12-alpine
|
||||||
@ -20,25 +40,8 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8077:5432"
|
- "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:
|
eth-server:
|
||||||
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
build:
|
build:
|
||||||
@ -47,16 +50,22 @@ services:
|
|||||||
- alpine:latest
|
- alpine:latest
|
||||||
- golang:1.13-alpine
|
- golang:1.13-alpine
|
||||||
environment:
|
environment:
|
||||||
|
IPLD_SERVER_GRAPHQL: "true"
|
||||||
|
IPLD_POSTGRAPHILEPATH: http://graphql:5000
|
||||||
|
ETH_SERVER_HTTPPATH: 0.0.0.0:8081
|
||||||
VDB_COMMAND: "serve"
|
VDB_COMMAND: "serve"
|
||||||
|
ETH_CHAIN_CONFIG: "/tmp/chain.json"
|
||||||
DATABASE_NAME: "vulcanize_public"
|
DATABASE_NAME: "vulcanize_public"
|
||||||
DATABASE_HOSTNAME: "db"
|
DATABASE_HOSTNAME: "db"
|
||||||
DATABASE_PORT: 5432
|
DATABASE_PORT: 5432
|
||||||
DATABASE_USER: "vdbm"
|
DATABASE_USER: "vdbm"
|
||||||
DATABASE_PASSWORD: "password"
|
DATABASE_PASSWORD: "password"
|
||||||
SERVER_WS_PATH: "0.0.0.0:8081"
|
ETH_CHAIN_ID: 4
|
||||||
SERVER_HTTP_PATH: "0.0.0.0:8082"
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ./chain.json
|
||||||
|
target: /tmp/chain.json
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8080:8080"
|
|
||||||
- "127.0.0.1:8081:8081"
|
- "127.0.0.1:8081:8081"
|
||||||
|
|
||||||
graphql:
|
graphql:
|
||||||
|
175
pkg/eth/api.go
175
pkg/eth/api.go
@ -18,9 +18,11 @@ package eth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
@ -38,8 +40,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/statediff"
|
"github.com/ethereum/go-ethereum/statediff"
|
||||||
"github.com/sirupsen/logrus"
|
"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"
|
"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
|
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
|
Uncles
|
||||||
@ -441,6 +456,14 @@ func (pea *PublicEthAPI) localGetTransactionReceipt(ctx context.Context, hash co
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
if len(receipts) <= int(index) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -526,9 +549,11 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
|||||||
for i, addr := range crit.Addresses {
|
for i, addr := range crit.Addresses {
|
||||||
addrStrs[i] = addr.String()
|
addrStrs[i] = addr.String()
|
||||||
}
|
}
|
||||||
topicStrSets := make([][]string, 4)
|
|
||||||
|
topicStrSets := make([][]string, len(crit.Topics))
|
||||||
for i, topicSet := range crit.Topics {
|
for i, topicSet := range crit.Topics {
|
||||||
if i > 3 {
|
if i > 3 {
|
||||||
|
topicStrSets = topicStrSets[:4]
|
||||||
// don't allow more than 4 topics
|
// don't allow more than 4 topics
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -541,14 +566,42 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
|||||||
Topics: topicStrSets,
|
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 we have a blockhash to filter on, fire off single retrieval query
|
||||||
if crit.BlockHash != nil {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Otherwise, create block range from criteria
|
||||||
// nil values are filled in; to request a single block have both ToBlock and FromBlock equal that number
|
// nil values are filled in; to request a single block have both ToBlock and FromBlock equal that number
|
||||||
startingBlock := crit.FromBlock
|
startingBlock := crit.FromBlock
|
||||||
@ -556,6 +609,7 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
|||||||
if startingBlock == nil {
|
if startingBlock == nil {
|
||||||
startingBlock = common.Big0
|
startingBlock = common.Big0
|
||||||
}
|
}
|
||||||
|
|
||||||
if endingBlock == nil {
|
if endingBlock == nil {
|
||||||
endingBlockInt, err := pea.B.Retriever.RetrieveLastBlockNumber()
|
endingBlockInt, err := pea.B.Retriever.RetrieveLastBlockNumber()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -563,71 +617,39 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
|
|||||||
}
|
}
|
||||||
endingBlock = big.NewInt(endingBlockInt)
|
endingBlock = big.NewInt(endingBlockInt)
|
||||||
}
|
}
|
||||||
|
|
||||||
start := startingBlock.Int64()
|
start := startingBlock.Int64()
|
||||||
end := endingBlock.Int64()
|
end := endingBlock.Int64()
|
||||||
blocks, topics, err := pea.getLogsForBlockRange(filter, start, end)
|
var logs []*types.Log
|
||||||
if err != nil {
|
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 nil, err
|
||||||
}
|
}
|
||||||
return extractLogsOfInterest(blocks, topics)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pea *PublicEthAPI) getLogsForBlockRange(filter ReceiptFilter, start, end int64) ([]ipfs.BlockModel, [][]string, error) {
|
return logs, err // need to return err variable so that we return the err = tx.Commit() assignment in the defer
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -651,6 +673,10 @@ func (pea *PublicEthAPI) GetBalance(ctx context.Context, address common.Address,
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return (*hexutil.Big)(big.NewInt(0)), nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, err
|
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) {
|
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)
|
storageVal, err := pea.B.GetStorageByNumberOrHash(ctx, address, common.HexToHash(key), blockNrOrHash)
|
||||||
if storageVal != nil && err == nil {
|
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 {
|
if pea.rpc != nil {
|
||||||
var res hexutil.Bytes
|
var res hexutil.Bytes
|
||||||
@ -677,6 +713,9 @@ func (pea *PublicEthAPI) GetStorageAt(ctx context.Context, address common.Addres
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return make([]byte, 32), nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,6 +732,10 @@ func (pea *PublicEthAPI) GetCode(ctx context.Context, address common.Address, bl
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return code, nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,16 @@ package eth_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
@ -168,7 +171,7 @@ var (
|
|||||||
"to": expectedTransaction3.To,
|
"to": expectedTransaction3.To,
|
||||||
"gasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].GasUsed),
|
"gasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].GasUsed),
|
||||||
"cumulativeGasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].CumulativeGasUsed),
|
"cumulativeGasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].CumulativeGasUsed),
|
||||||
"contractAddress": nil,
|
"contractAddress": test_helpers.ContractAddress,
|
||||||
"logs": test_helpers.MockReceipts[2].Logs,
|
"logs": test_helpers.MockReceipts[2].Logs,
|
||||||
"logsBloom": test_helpers.MockReceipts[2].Bloom,
|
"logsBloom": test_helpers.MockReceipts[2].Bloom,
|
||||||
"root": hexutil.Bytes(test_helpers.MockReceipts[2].PostState),
|
"root": hexutil.Bytes(test_helpers.MockReceipts[2].PostState),
|
||||||
@ -177,8 +180,9 @@ var (
|
|||||||
|
|
||||||
var _ = Describe("API", func() {
|
var _ = Describe("API", func() {
|
||||||
var (
|
var (
|
||||||
db *postgres.DB
|
db *postgres.DB
|
||||||
api *eth.PublicEthAPI
|
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
|
// 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()
|
// 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()
|
db, err = shared.SetupDB()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
indexAndPublisher := eth2.NewIPLDPublisher(db)
|
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())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
api = eth.NewPublicEthAPI(backend, nil, false)
|
api = eth.NewPublicEthAPI(backend, nil, false)
|
||||||
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
|
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
|
||||||
@ -271,10 +279,10 @@ var _ = Describe("API", func() {
|
|||||||
Expect(val).To(Equal(block[key]))
|
Expect(val).To(Equal(block[key]))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
It("Throws an error if a block cannot be found", func() {
|
It("Returns `nil` if a block cannot be found", func() {
|
||||||
_, err := api.GetBlockByNumber(ctx, wrongNumber, false)
|
block, err := api.GetBlockByNumber(ctx, wrongNumber, false)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(block).To(BeNil())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -303,10 +311,10 @@ var _ = Describe("API", func() {
|
|||||||
Expect(val).To(Equal(block[key]))
|
Expect(val).To(Equal(block[key]))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
It("Throws an error if a block cannot be found", func() {
|
It("Returns `nil` if a block cannot be found", func() {
|
||||||
_, err := api.GetBlockByHash(ctx, randomHash, false)
|
block, err := api.GetBlockByHash(ctx, randomHash, false)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(block).To(BeZero())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -325,10 +333,10 @@ var _ = Describe("API", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(uncle2).To(Equal(expectedUncle2))
|
Expect(uncle2).To(Equal(expectedUncle2))
|
||||||
})
|
})
|
||||||
It("Throws an error if an block for blocknumber cannot be found", func() {
|
It("Returns `nil` if an block for block number cannot be found", func() {
|
||||||
_, err := api.GetUncleByBlockNumberAndIndex(ctx, wrongNumber, 0)
|
block, err := api.GetUncleByBlockNumberAndIndex(ctx, wrongNumber, 0)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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() {
|
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)
|
uncle, err := api.GetUncleByBlockNumberAndIndex(ctx, number, 2)
|
||||||
@ -346,10 +354,10 @@ var _ = Describe("API", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(uncle2).To(Equal(expectedUncle2))
|
Expect(uncle2).To(Equal(expectedUncle2))
|
||||||
})
|
})
|
||||||
It("Throws an error if an block for blockhash cannot be found", func() {
|
It("Returns `nil` if a block for blockhash cannot be found", func() {
|
||||||
_, err := api.GetUncleByBlockHashAndIndex(ctx, randomHash, 0)
|
block, err := api.GetUncleByBlockHashAndIndex(ctx, randomHash, 0)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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() {
|
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)
|
uncle, err := api.GetUncleByBlockHashAndIndex(ctx, blockHash, 2)
|
||||||
@ -361,6 +369,7 @@ var _ = Describe("API", func() {
|
|||||||
Describe("eth_getUncleCountByBlockNumber", func() {
|
Describe("eth_getUncleCountByBlockNumber", func() {
|
||||||
It("Retrieves the number of uncles for the canonical block with the provided number", func() {
|
It("Retrieves the number of uncles for the canonical block with the provided number", func() {
|
||||||
count := api.GetUncleCountByBlockNumber(ctx, number)
|
count := api.GetUncleCountByBlockNumber(ctx, number)
|
||||||
|
Expect(*count).NotTo(Equal(nil))
|
||||||
Expect(uint64(*count)).To(Equal(uint64(2)))
|
Expect(uint64(*count)).To(Equal(uint64(2)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -368,6 +377,7 @@ var _ = Describe("API", func() {
|
|||||||
Describe("eth_getUncleCountByBlockHash", func() {
|
Describe("eth_getUncleCountByBlockHash", func() {
|
||||||
It("Retrieves the number of uncles for the block with the provided hash", func() {
|
It("Retrieves the number of uncles for the block with the provided hash", func() {
|
||||||
count := api.GetUncleCountByBlockHash(ctx, blockHash)
|
count := api.GetUncleCountByBlockHash(ctx, blockHash)
|
||||||
|
Expect(*count).NotTo(Equal(nil))
|
||||||
Expect(uint64(*count)).To(Equal(uint64(2)))
|
Expect(uint64(*count)).To(Equal(uint64(2)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -948,8 +958,17 @@ var _ = Describe("API", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||||
})
|
})
|
||||||
It("Throws an error for an account it cannot find the balance for", func() {
|
It("Retrieves the eth balance for the non-existing account address at the block with the provided hash", func() {
|
||||||
_, err := api.GetBalance(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
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())
|
Expect(err).To(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -965,9 +984,10 @@ var _ = Describe("API", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||||
})
|
})
|
||||||
It("Throws an error for an account it cannot find the code for", func() {
|
It("Returns `nil` for an account it cannot find the code for", func() {
|
||||||
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
code, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(code).To(BeEmpty())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -18,6 +18,7 @@ package eth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -51,6 +52,8 @@ import (
|
|||||||
var (
|
var (
|
||||||
errPendingBlockNumber = errors.New("pending block number not supported")
|
errPendingBlockNumber = errors.New("pending block number not supported")
|
||||||
errNegativeBlockNumber = errors.New("negative 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 (
|
const (
|
||||||
@ -175,7 +178,11 @@ func (b *Backend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Bl
|
|||||||
if header == nil {
|
if header == nil {
|
||||||
return nil, errors.New("header for hash not found")
|
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 nil, errors.New("hash is not currently canonical")
|
||||||
}
|
}
|
||||||
return header, nil
|
return header, nil
|
||||||
@ -198,9 +205,9 @@ func (b *Backend) GetTd(blockHash common.Hash) (*big.Int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CurrentBlock returns the current block
|
// CurrentBlock returns the current block
|
||||||
func (b *Backend) CurrentBlock() *types.Block {
|
func (b *Backend) CurrentBlock() (*types.Block, error) {
|
||||||
block, _ := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber)
|
block, err := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber)
|
||||||
return block
|
return block, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockByNumberOrHash returns block by number or hash
|
// BlockByNumberOrHash returns block by number or hash
|
||||||
@ -216,7 +223,11 @@ func (b *Backend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Blo
|
|||||||
if header == nil {
|
if header == nil {
|
||||||
return nil, errors.New("header for hash not found")
|
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 nil, errors.New("hash is not currently canonical")
|
||||||
}
|
}
|
||||||
block, err := b.BlockByHash(ctx, hash)
|
block, err := b.BlockByHash(ctx, hash)
|
||||||
@ -254,14 +265,20 @@ func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber
|
|||||||
return nil, errNegativeBlockNumber
|
return nil, errNegativeBlockNumber
|
||||||
}
|
}
|
||||||
// Get the canonical hash
|
// Get the canonical hash
|
||||||
canonicalHash := b.GetCanonicalHash(uint64(number))
|
canonicalHash, err := b.GetCanonicalHash(uint64(number))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Retrieve all the CIDs for the block
|
// 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)
|
// 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)
|
headerCID, uncleCIDs, txCIDs, rctCIDs, err := b.Retriever.RetrieveBlockByHash(canonicalHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +302,9 @@ func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber
|
|||||||
var headerIPLD ipfs.BlockModel
|
var headerIPLD ipfs.BlockModel
|
||||||
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
|
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var header types.Header
|
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
|
// Retrieve all the CIDs for the block
|
||||||
headerCID, uncleCIDs, txCIDs, rctCIDs, err := b.Retriever.RetrieveBlockByHash(hash)
|
headerCID, uncleCIDs, txCIDs, rctCIDs, err := b.Retriever.RetrieveBlockByHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,6 +393,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
|||||||
var headerIPLD ipfs.BlockModel
|
var headerIPLD ipfs.BlockModel
|
||||||
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
|
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var header types.Header
|
var header types.Header
|
||||||
@ -381,6 +407,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
|||||||
var uncleIPLDs []ipfs.BlockModel
|
var uncleIPLDs []ipfs.BlockModel
|
||||||
uncleIPLDs, err = b.Fetcher.FetchUncles(tx, uncleCIDs)
|
uncleIPLDs, err = b.Fetcher.FetchUncles(tx, uncleCIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var uncles []*types.Header
|
var uncles []*types.Header
|
||||||
@ -396,6 +425,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
|||||||
var txIPLDs []ipfs.BlockModel
|
var txIPLDs []ipfs.BlockModel
|
||||||
txIPLDs, err = b.Fetcher.FetchTrxs(tx, txCIDs)
|
txIPLDs, err = b.Fetcher.FetchTrxs(tx, txCIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var transactions []*types.Transaction
|
var transactions []*types.Transaction
|
||||||
@ -411,6 +443,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
|||||||
var rctIPLDs []ipfs.BlockModel
|
var rctIPLDs []ipfs.BlockModel
|
||||||
rctIPLDs, err = b.Fetcher.FetchRcts(tx, rctCIDs)
|
rctIPLDs, err = b.Fetcher.FetchRcts(tx, rctCIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var receipts []*types.Receipt
|
var receipts []*types.Receipt
|
||||||
@ -492,7 +527,11 @@ func (b *Backend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHas
|
|||||||
if header == nil {
|
if header == nil {
|
||||||
return nil, nil, errors.New("header for hash not found")
|
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")
|
return nil, nil, errors.New("hash is not currently canonical")
|
||||||
}
|
}
|
||||||
stateDb, err := state.New(header.Root, b.StateDatabase, nil)
|
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
|
// 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
|
var hashResult string
|
||||||
if err := b.DB.Get(&hashResult, RetrieveCanonicalBlockHashByNumber, number); err != nil {
|
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 {
|
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
|
// 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) {
|
func (b *Backend) GetAccountByNumberOrHash(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*state.Account, error) {
|
||||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
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 {
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
return b.GetAccountByHash(ctx, address, hash)
|
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
|
// 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) {
|
func (b *Backend) GetAccountByNumber(ctx context.Context, address common.Address, blockNumber rpc.BlockNumber) (*state.Account, error) {
|
||||||
hash := b.GetCanonicalHash(number)
|
var err error
|
||||||
if hash == (common.Hash{}) {
|
number := blockNumber.Int64()
|
||||||
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
|
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)
|
return b.GetAccountByHash(ctx, address, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountByHash returns the account object for the provided address at the block with the provided 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) {
|
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)
|
_, accountRlp, err := b.IPLDRetriever.RetrieveAccountByAddressAndBlockHash(address, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
acct := new(state.Account)
|
acct := new(state.Account)
|
||||||
return acct, rlp.DecodeBytes(accountRlp, acct)
|
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
|
// 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) {
|
func (b *Backend) GetCodeByNumberOrHash(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) ([]byte, error) {
|
||||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
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 {
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
return b.GetCodeByHash(ctx, address, hash)
|
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
|
// 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) {
|
func (b *Backend) GetCodeByNumber(ctx context.Context, address common.Address, blockNumber rpc.BlockNumber) ([]byte, error) {
|
||||||
hash := b.GetCanonicalHash(number)
|
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{}) {
|
if hash == (common.Hash{}) {
|
||||||
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
|
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
|
// 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 {
|
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 {
|
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")
|
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
|
// 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) {
|
func (b *Backend) GetStorageByNumber(ctx context.Context, address common.Address, key common.Hash, blockNumber rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||||
hash := b.GetCanonicalHash(number)
|
var err error
|
||||||
if hash == (common.Hash{}) {
|
number := blockNumber.Int64()
|
||||||
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
|
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
|
// 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) {
|
func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address, key, hash common.Hash) (hexutil.Bytes, error) {
|
||||||
_, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(address, storageLeafKey, hash)
|
_, 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
|
return storageRlp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,12 +23,12 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
"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
|
// extractLogsOfInterest returns logs from the receipt IPLD
|
||||||
func extractLogsOfInterest(rctIPLDs []ipfs.BlockModel, wantedTopics [][]string) ([]*types.Log, error) {
|
func extractLogsOfInterest(config *params.ChainConfig, blockHash common.Hash, blockNumber uint64,
|
||||||
var logs []*types.Log
|
txs types.Transactions, rctIPLDs []ipfs.BlockModel, filter ReceiptFilter) ([]*types.Log, error) {
|
||||||
for _, rctIPLD := range rctIPLDs {
|
receipts := make(types.Receipts, len(rctIPLDs))
|
||||||
rctRLP := rctIPLD
|
|
||||||
var rct types.Receipt
|
for i, rctBytes := range rctIPLDs {
|
||||||
if err := rlp.DecodeBytes(rctRLP.Data, &rct); err != nil {
|
rct := new(types.Receipt)
|
||||||
|
if err := rlp.DecodeBytes(rctBytes.Data, rct); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, log := range rct.Logs {
|
receipts[i] = rct
|
||||||
if wanted := wantedLog(wantedTopics, log.Topics); wanted == true {
|
}
|
||||||
logs = append(logs, log)
|
|
||||||
}
|
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
|
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
|
// returns true if the log matches on the filter
|
||||||
func wantedLog(wantedTopics [][]string, actualTopics []common.Hash) bool {
|
func wantedLog(wantedTopics [][]string, actualTopics []common.Hash) bool {
|
||||||
// actualTopics will always have length <= 4
|
// 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())
|
args = append(args, blockHash.String())
|
||||||
id++
|
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 {
|
if len(rctFilter.LogAddresses) > 0 {
|
||||||
// Filter on log contract addresses if there are any
|
// Filter on log contract addresses if there are any
|
||||||
pgStr += fmt.Sprintf(` AND ((receipt_cids.log_contracts && $%d::VARCHAR(66)[]`, id)
|
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))
|
args = append(args, pq.Array(trxIds))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pgStr += ` ORDER BY transaction_cids.index`
|
pgStr += ` ORDER BY transaction_cids.index`
|
||||||
receiptCids := make([]eth.ReceiptModel, 0)
|
receiptCids := make([]eth.ReceiptModel, 0)
|
||||||
return receiptCids, tx.Select(&receiptCids, pgStr, args...)
|
return receiptCids, tx.Select(&receiptCids, pgStr, args...)
|
||||||
|
@ -237,6 +237,7 @@ var _ = Describe("Retriever", func() {
|
|||||||
Expect(empty).ToNot(BeTrue())
|
Expect(empty).ToNot(BeTrue())
|
||||||
Expect(len(cids)).To(Equal(1))
|
Expect(len(cids)).To(Equal(1))
|
||||||
Expect(cids[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
Expect(cids[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
||||||
|
|
||||||
expectedHeaderCID := test_helpers.MockCIDWrapper.Header
|
expectedHeaderCID := test_helpers.MockCIDWrapper.Header
|
||||||
expectedHeaderCID.ID = cids[0].Header.ID
|
expectedHeaderCID.ID = cids[0].Header.ID
|
||||||
expectedHeaderCID.NodeID = cids[0].Header.NodeID
|
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[1].CID)).To(BeTrue())
|
||||||
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, test_helpers.MockCIDWrapper.Receipts[2].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))
|
Expect(len(cids[0].StateNodes)).To(Equal(2))
|
||||||
|
|
||||||
for _, stateNode := range cids[0].StateNodes {
|
for _, stateNode := range cids[0].StateNodes {
|
||||||
if stateNode.CID == test_helpers.State1CID.String() {
|
if stateNode.CID == test_helpers.State1CID.String() {
|
||||||
Expect(stateNode.StateKey).To(Equal(common.BytesToHash(test_helpers.ContractLeafKey).Hex()))
|
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"
|
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/shared"
|
"github.com/vulcanize/ipld-eth-indexer/pkg/shared"
|
||||||
|
|
||||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||||
)
|
)
|
||||||
@ -60,6 +59,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _ = Describe("eth state reading tests", func() {
|
var _ = Describe("eth state reading tests", func() {
|
||||||
|
const chainLength = 5
|
||||||
var (
|
var (
|
||||||
blocks []*types.Block
|
blocks []*types.Block
|
||||||
receipts []types.Receipts
|
receipts []types.Receipts
|
||||||
@ -80,13 +80,13 @@ var _ = Describe("eth state reading tests", func() {
|
|||||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||||
ChainConfig: chainConfig,
|
ChainConfig: chainConfig,
|
||||||
VmConfig: vm.Config{},
|
VmConfig: vm.Config{},
|
||||||
RPCGasCap: big.NewInt(10000000000),
|
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
|
||||||
})
|
})
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
api = eth.NewPublicEthAPI(backend, nil, false)
|
api = eth.NewPublicEthAPI(backend, nil, false)
|
||||||
|
|
||||||
// make the test blockchain (and state)
|
// 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{
|
params := statediff.Params{
|
||||||
IntermediateStateNodes: true,
|
IntermediateStateNodes: true,
|
||||||
IntermediateStorageNodes: 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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(1))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(1))
|
|
||||||
Expect(err).To(HaveOccurred())
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(1))
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(err).ToNot(HaveOccurred())
|
||||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(1))
|
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(1))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock1))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(2))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(2))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(2))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(2))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(2))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(2))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(2))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(3))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(3))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(3))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(3))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(3))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(3))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(3))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(4))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(4))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(4))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(4))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(4))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(4))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(4))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(5))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(5))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(5))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(5))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(5))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(5))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(5))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
|
||||||
Expect(err).To(HaveOccurred())
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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))
|
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock1))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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))
|
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedContractBalance))
|
Expect(bal).To(Equal(expectedContractBalance))
|
||||||
|
|
||||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
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() {
|
It("Returns `0` 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))
|
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(0))
|
|
||||||
Expect(err).To(HaveOccurred())
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(err).ToNot(HaveOccurred())
|
||||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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() {
|
It("Returns `0` for 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))
|
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
|
||||||
Expect(err).To(HaveOccurred())
|
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(err).ToNot(HaveOccurred())
|
||||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||||
})
|
})
|
||||||
It("Throws an error for an account it cannot find the code for", func() {
|
It("Returns `nil` for an account it cannot find the code for", func() {
|
||||||
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
code, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(code).To(BeEmpty())
|
||||||
})
|
})
|
||||||
It("Throws an error for a contract that doesn't exist at this hieght", func() {
|
It("Returns `nil` for a contract that doesn't exist at this height", func() {
|
||||||
_, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
code, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(code).To(BeEmpty())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("eth_getStorageAt", func() {
|
Describe("eth_getStorageAt", func() {
|
||||||
It("Throws an error if it tries to access a contract which does not exist", func() {
|
It("Returns empty slice 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))
|
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
|
||||||
|
|
||||||
_, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
|
storage, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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() {
|
It("Returns empty slice 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))
|
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
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() {
|
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
|
// 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())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
expectedRes := hexutil.Bytes(common.Hex2Bytes("01"))
|
expectedRes := hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
Expect(val).To(Equal(expectedRes))
|
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())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("03"))
|
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
||||||
Expect(val).To(Equal(expectedRes))
|
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())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("09"))
|
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000009"))
|
||||||
Expect(val).To(Equal(expectedRes))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(val).To(Equal(hexutil.Bytes{}))
|
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() {
|
Describe("eth_getHeaderByNumber", func() {
|
||||||
|
@ -428,11 +428,12 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Ad
|
|||||||
return accountResult.CID, i[1].([]byte), nil
|
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
|
// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash
|
||||||
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(address common.Address, storageLeafKey, hash common.Hash) (string, []byte, error) {
|
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, error) {
|
||||||
storageResult := new(nodeInfo)
|
storageResult := new(nodeInfo)
|
||||||
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
|
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
|
return "", nil, err
|
||||||
}
|
}
|
||||||
if storageResult.Removed {
|
if storageResult.Removed {
|
||||||
|
@ -34,7 +34,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/statediff/testhelpers"
|
"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"
|
"github.com/multiformats/go-multihash"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts)
|
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())
|
MockHeaderRlp, _ = rlp.EncodeToBytes(MockBlock.Header())
|
||||||
MockChildHeader = types.Header{
|
MockChildHeader = types.Header{
|
||||||
Time: 0,
|
Time: 0,
|
||||||
@ -104,15 +104,22 @@ var (
|
|||||||
mockTopic21 = common.HexToHash("0x05")
|
mockTopic21 = common.HexToHash("0x05")
|
||||||
mockTopic22 = common.HexToHash("0x07")
|
mockTopic22 = common.HexToHash("0x07")
|
||||||
MockLog1 = &types.Log{
|
MockLog1 = &types.Log{
|
||||||
Address: Address,
|
Address: Address,
|
||||||
Topics: []common.Hash{mockTopic11, mockTopic12},
|
Topics: []common.Hash{mockTopic11, mockTopic12},
|
||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
|
BlockNumber: BlockNumber.Uint64(),
|
||||||
|
TxIndex: 0,
|
||||||
|
Index: 0,
|
||||||
}
|
}
|
||||||
MockLog2 = &types.Log{
|
MockLog2 = &types.Log{
|
||||||
Address: AnotherAddress,
|
Address: AnotherAddress,
|
||||||
Topics: []common.Hash{mockTopic21, mockTopic22},
|
Topics: []common.Hash{mockTopic21, mockTopic22},
|
||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
|
BlockNumber: BlockNumber.Uint64(),
|
||||||
|
TxIndex: 1,
|
||||||
|
Index: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderCID, _ = ipld.RawdataToCid(ipld.MEthHeader, MockHeaderRlp, multihash.KECCAK_256)
|
HeaderCID, _ = ipld.RawdataToCid(ipld.MEthHeader, MockHeaderRlp, multihash.KECCAK_256)
|
||||||
HeaderMhKey = shared.MultihashKeyFromCID(HeaderCID)
|
HeaderMhKey = shared.MultihashKeyFromCID(HeaderCID)
|
||||||
Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, MockTransactions.GetRlp(0), multihash.KECCAK_256)
|
Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, MockTransactions.GetRlp(0), multihash.KECCAK_256)
|
||||||
@ -375,6 +382,8 @@ var (
|
|||||||
StateNodes: MockStateNodes,
|
StateNodes: MockStateNodes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reward = eth.CalcEthBlockReward(MockBlock.Header(), MockBlock.Uncles(), MockBlock.Transactions(), MockReceipts)
|
||||||
|
|
||||||
MockCIDWrapper = ð2.CIDWrapper{
|
MockCIDWrapper = ð2.CIDWrapper{
|
||||||
BlockNumber: new(big.Int).Set(BlockNumber),
|
BlockNumber: new(big.Int).Set(BlockNumber),
|
||||||
Header: eth.HeaderModel{
|
Header: eth.HeaderModel{
|
||||||
@ -384,7 +393,7 @@ var (
|
|||||||
CID: HeaderCID.String(),
|
CID: HeaderCID.String(),
|
||||||
MhKey: HeaderMhKey,
|
MhKey: HeaderMhKey,
|
||||||
TotalDifficulty: MockBlock.Difficulty().String(),
|
TotalDifficulty: MockBlock.Difficulty().String(),
|
||||||
Reward: "5312500000000000000",
|
Reward: Reward.String(),
|
||||||
StateRoot: MockBlock.Root().String(),
|
StateRoot: MockBlock.Root().String(),
|
||||||
RctRoot: MockBlock.ReceiptHash().String(),
|
RctRoot: MockBlock.ReceiptHash().String(),
|
||||||
TxRoot: MockBlock.TxHash().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
|
// createTransactionsAndReceipts is a helper function to generate signed mock transactions and mock receipts with mock logs
|
||||||
func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common.Address) {
|
func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common.Address) {
|
||||||
// make transactions
|
// make transactions
|
||||||
@ -519,13 +539,26 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
|||||||
}
|
}
|
||||||
// make receipts
|
// make receipts
|
||||||
mockReceipt1 := types.NewReceipt(common.HexToHash("0x0").Bytes(), false, 50)
|
mockReceipt1 := types.NewReceipt(common.HexToHash("0x0").Bytes(), false, 50)
|
||||||
|
|
||||||
|
hash1 := signedTrx1.Hash()
|
||||||
|
MockLog1.TxHash = hash1
|
||||||
|
|
||||||
mockReceipt1.Logs = []*types.Log{MockLog1}
|
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)
|
mockReceipt2 := types.NewReceipt(common.HexToHash("0x1").Bytes(), false, 100)
|
||||||
|
hash2 := signedTrx2.Hash()
|
||||||
|
MockLog2.TxHash = hash2
|
||||||
|
|
||||||
mockReceipt2.Logs = []*types.Log{MockLog2}
|
mockReceipt2.Logs = []*types.Log{MockLog2}
|
||||||
mockReceipt2.TxHash = signedTrx2.Hash()
|
mockReceipt2.TxHash = hash2
|
||||||
mockReceipt3 := types.NewReceipt(common.HexToHash("0x2").Bytes(), false, 75)
|
mockReceipt2.GasUsed = mockReceipt2.CumulativeGasUsed - mockReceipt1.CumulativeGasUsed
|
||||||
|
|
||||||
|
mockReceipt3 := types.NewReceipt(common.HexToHash("0x2").Bytes(), false, 175)
|
||||||
mockReceipt3.Logs = []*types.Log{}
|
mockReceipt3.Logs = []*types.Log{}
|
||||||
mockReceipt3.TxHash = signedTrx3.Hash()
|
mockReceipt3.TxHash = signedTrx3.Hash()
|
||||||
|
mockReceipt3.GasUsed = mockReceipt3.CumulativeGasUsed - mockReceipt2.CumulativeGasUsed
|
||||||
|
|
||||||
return types.Transactions{signedTrx1, signedTrx2, signedTrx3}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3}, SenderAddr
|
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 {
|
if args.To != nil {
|
||||||
to = rpc.BlockNumber(*args.To)
|
to = rpc.BlockNumber(*args.To)
|
||||||
} else {
|
} 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 {
|
if to < from {
|
||||||
return []*Block{}, nil
|
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)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
}
|
}
|
||||||
extapiURL := fmt.Sprintf("http://%v/", addr)
|
extapiURL := fmt.Sprintf("http://%v/", addr)
|
||||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
log.Infof("HTTP endpoint opened %s", extapiURL)
|
||||||
|
|
||||||
return srv, err
|
return srv, err
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package serve
|
package serve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
@ -54,14 +55,31 @@ const (
|
|||||||
|
|
||||||
// Config struct
|
// Config struct
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DB *postgres.DB
|
DB *postgres.DB
|
||||||
DBConfig postgres.Config
|
DBConfig postgres.Config
|
||||||
WSEndpoint string
|
|
||||||
HTTPEndpoint string
|
WSEnabled bool
|
||||||
IPCEndpoint string
|
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
|
ChainConfig *params.ChainConfig
|
||||||
DefaultSender *common.Address
|
DefaultSender *common.Address
|
||||||
RPCGasCap *big.Int
|
RPCGasCap *big.Int
|
||||||
|
EthHttpEndpoint string
|
||||||
Client *rpc.Client
|
Client *rpc.Client
|
||||||
SupportStateDiff bool
|
SupportStateDiff bool
|
||||||
}
|
}
|
||||||
@ -71,9 +89,6 @@ type Config struct {
|
|||||||
func NewConfig() (*Config, error) {
|
func NewConfig() (*Config, error) {
|
||||||
c := new(Config)
|
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.httpPath", shared.ETH_HTTP_PATH)
|
||||||
viper.BindEnv("ethereum.defaultSender", ETH_DEFAULT_SENDER_ADDR)
|
viper.BindEnv("ethereum.defaultSender", ETH_DEFAULT_SENDER_ADDR)
|
||||||
viper.BindEnv("ethereum.rpcGasCap", ETH_RPC_GAS_CAP)
|
viper.BindEnv("ethereum.rpcGasCap", ETH_RPC_GAS_CAP)
|
||||||
@ -83,32 +98,91 @@ func NewConfig() (*Config, error) {
|
|||||||
c.DBConfig.Init()
|
c.DBConfig.Init()
|
||||||
|
|
||||||
ethHTTP := viper.GetString("ethereum.httpPath")
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.Client = cli
|
c.Client = cli
|
||||||
c.SupportStateDiff = viper.GetBool("ethereum.supportsStateDiff")
|
c.SupportStateDiff = viper.GetBool("ethereum.supportsStateDiff")
|
||||||
|
c.EthHttpEndpoint = ethHTTPEndpoint
|
||||||
|
|
||||||
wsPath := viper.GetString("server.wsPath")
|
// websocket server
|
||||||
if wsPath == "" {
|
wsEnabled := viper.GetBool("eth.server.ws")
|
||||||
wsPath = "127.0.0.1:8080"
|
if wsEnabled {
|
||||||
}
|
wsPath := viper.GetString("eth.server.wsPath")
|
||||||
c.WSEndpoint = wsPath
|
if wsPath == "" {
|
||||||
ipcPath := viper.GetString("server.ipcPath")
|
wsPath = "127.0.0.1:8080"
|
||||||
if ipcPath == "" {
|
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
ipcPath = filepath.Join(home, ".vulcanize/vulcanize.ipc")
|
c.WSEndpoint = wsPath
|
||||||
}
|
}
|
||||||
c.IPCEndpoint = ipcPath
|
c.WSEnabled = wsEnabled
|
||||||
httpPath := viper.GetString("server.httpPath")
|
|
||||||
if httpPath == "" {
|
// ipc server
|
||||||
httpPath = "127.0.0.1:8081"
|
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)
|
overrideDBConnConfig(&c.DBConfig)
|
||||||
serveDB := utils.LoadPostgres(c.DBConfig, nodeInfo, false)
|
serveDB := utils.LoadPostgres(c.DBConfig, nodeInfo, false)
|
||||||
prom.RegisterDBCollector(c.DBConfig.Name, serveDB.DB)
|
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