Compare commits

..
Author SHA1 Message Date
Ramil Amerzyanov 000c0ef066 bump version (#89) 2021-08-12 13:46:29 +03:00
Arijit Das e00e602098 Merge pull request #83 from vulcanize/marshall-binary
Update RPC api and backend to support the EIP-2930 and EIP-1559.
2021-08-05 11:04:57 +05:30
Arijit Das 148addb24d Update comment for query change 2021-08-05 08:25:47 +05:30
Arijit Das 2630e2d8dc Update the ipld indexer dependency 2021-08-05 08:24:40 +05:30
Arijit Das 924d0be0b9 Update RPC api and backend to support the EIP-2930 and EIP-1559. 2021-07-27 17:37:50 +05:30
Ashwin PhatakandArijit Das 70f7face75 getLogs API changes to return txHash, make contract arg optional. (#81)
* getLogs API changes to return txHash, make contract arg optional.

* Populate log index.

* Add test for txn hash in GetLogs request.

* Convert tx string to common.Hash after fetching.

Co-authored-by: Arijit Das <arijitad.in@gmail.com>
2021-07-26 15:43:38 +05:30
Arijit Das afc63ac960 Merge pull request #77 from vulcanize/ashwinp-gql-get-storage-n-logs
GQL API for getStorageAt and getLogs
2021-07-05 12:43:53 +05:30
Arijit Das f1a61d0991 Add graphql tests. 2021-07-02 14:35:01 +05:30
Arijit Das 9a5581b543 Fix failing unit tests. 2021-06-29 12:59:37 +05:30
Arijit Das 1d18d1fed8 Update gopsutil to fix build on mac. 2021-06-29 12:56:26 +05:30
Ashwin Phatak 0a14bd9f0f Update method used to get storage slot. 2021-06-29 12:23:10 +05:30
Ashwin Phatak a3ca08b653 Return empty result instead of error if slot not found in storage table. 2021-06-28 12:51:38 +05:30
Ashwin Phatak b2828a814f Compute leaf key from slot. 2021-06-28 12:51:38 +05:30
Ashwin Phatak a284a566d5 Get storage API, with storage leaf CID and raw IPLD block. 2021-06-28 12:51:35 +05:30
Ashwin Phatak b90fcb53e6 Get logs API, with receipt CID and raw IPLD block. 2021-06-28 12:04:46 +05:30
Ashwin Phatak 18266c4f9d Get receipt CID and block data for logs. 2021-06-28 12:04:46 +05:30
Arijit Das 42f066772b Add Integration Test
Add Integration Test
2021-06-28 12:00:14 +05:30
Arijit Das 9aaea6e016 Address comments. 2021-06-18 17:13:46 +05:30
Arijit Das 969da82f6e Merge remote-tracking branch 'origin/master' into endpoints 2021-06-18 16:11:11 +05:30
Arijit Das f7fe3c2fc1 Merge pull request #53 from vulcanize/integration_tests
integration test
2021-06-18 12:29:19 +05:30
Arijit Das c4f7fa2821 Address comments. 2021-06-18 12:12:29 +05:30
Arijit Das b1a6fb0514 Fix typo. 2021-06-14 10:55:23 +05:30
Arijit Das e7744cce4b Add test scripts. 2021-06-14 10:26:10 +05:30
Arijit Das 491d779d58 Optimize docker build. 2021-06-10 13:13:01 +05:30
Arijit Das 2c1bc557e5 Add test for GetStorageAt RPC. 2021-06-10 11:51:38 +05:30
Arijit Das 9cf75ca66c Add StorageAt negative test cases. 2021-06-10 10:06:27 +05:30
Arijit Das 3b3716499f Fix and add integration test. 2021-06-10 08:50:18 +05:30
Arijit Das b3ef2934b7 Restore integration test target in Makefile. 2021-06-09 09:44:00 +05:30
Arijit Das 8c16dc2b35 Fix get logs unit test. 2021-06-09 09:12:46 +05:30
Arijit Das 7babaf6c05 Fix failing test 2021-06-04 00:01:25 +05:30
ramil 954e028306 custom comparation for tx and block (exclude time)
enrich receipts and logs with non-rlp encoded data (block number, block hash, etc)
2021-04-29 16:33:27 +03:00
Ian Norden 6844c60f7b Merge pull request #60 from vulcanize/shadowing
minor refactor; fix error shadowing
2021-04-23 08:41:49 -05:00
ramil 97d8c4dc86 integration tests: fix issue with eth_call (pass chain config via json file to match geth chain config)
added additional test case: gets balance for a non-existing account without block number
2021-04-23 12:46:35 +03:00
ramil 1141f3909c unit test fix for eth_getStorageAt 2021-04-21 22:42:11 +03:00
ramil c62799fdd6 integration test: use BeforeEach to deploy contract and perform transactions 2021-04-21 22:26:55 +03:00
ramil cef4b1ddc6 fix storageAt json-rpc endpoint 2021-04-21 22:09:57 +03:00
ramil 39141bd30d fix quiering json-rpc methods eth_getStorageAt, eth_getBalance without block number 2021-04-21 18:00:01 +03:00
ramil 0aa0f38c93 integration tests: eth call 2021-04-21 17:44:38 +03:00
ramil 8ac6d48772 integration tests: get storage 2021-04-21 16:38:02 +03:00
ramil bcbd2de5f3 integration tests: get balance 2021-04-21 16:05:24 +03:00
ramil d09b756768 fix unit tests for getCode method 2021-04-21 15:11:00 +03:00
ramil 36b62f9b72 Merge remote-tracking branch 'origin/integration_tests' into integration_tests 2021-04-21 14:16:56 +03:00
ramil 9b960a105f fix CodeAt without block number 2021-04-21 14:16:47 +03:00
Ramil Amerzyanov 2e5286baef update migration version 2021-04-20 14:17:09 +03:00
ramil f8b9d9475e add eth_chainId JSON-RPC endpoint 2021-04-20 00:52:58 +03:00
ramil 9d590e15bc fix race conditions in tests, call contract service instead of hardcoded values 2021-04-19 23:07:38 +03:00
Ian Norden d5cf74e202 minor refactor; fix error shadowing 2021-04-19 09:34:38 -05:00
ramil 7d88e78fa8 test eth_chainId 2021-04-19 15:40:42 +03:00
ramil 9d6791706d fix unit tests 2021-04-19 15:00:28 +03:00
ramil 50d53535bb integration test: run integration tests in GA 2021-04-19 14:43:21 +03:00
ramil c813ff00f4 integration test: fix unit tests 2021-04-19 14:24:58 +03:00
ramil 53f06a66f5 integration test: fix returning error when block doesn't exist 2021-04-19 13:16:59 +03:00
ramil 3c57fa1064 integration test: get block, tx, receipt, filter logs, code at 2021-04-19 11:32:36 +03:00
ramil 141b3eaffe integration test: initial commit 2021-04-16 16:57:11 +03:00
ramil 31cbaec567 fix serve command for ipld graphql server 2021-04-16 16:56:02 +03:00
ramil 75debec01a remove proxy command 2021-04-16 16:02:04 +03:00
ramil 07519468e9 fix serve command 2021-04-16 01:32:29 +03:00
ramil 524ab42674 normilize CLI options, environment variables
merge gap-filler library to "serve" commnad
2021-04-14 18:53:44 +03:00
Ian Norden 325516da70 Merge pull request #46 from vulcanize/get_logs
eth_getLogs use FilterCriteria not FilterQuery
2021-04-13 10:26:17 -05:00
Ian Norden 08df7beca3 eth_getLogs use FilterCriteria not FilterQuery 2021-04-11 23:12:20 -05:00
49 changed files with 30847 additions and 558 deletions
+26 -1
View File
@@ -11,7 +11,7 @@ jobs:
- name: Run docker build
run: make docker-build
test:
name: Run integration tests
name: Run unit tests
env:
GOPATH: /tmp/go
strategy:
@@ -33,3 +33,28 @@ jobs:
run: |
sleep 10
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=127.0.0.1 make test
integrationtest:
name: Run integration tests
env:
GOPATH: /tmp/go
strategy:
matrix:
go-version: [1.15.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Create GOPATH
run: mkdir -p /tmp/go
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v2
- name: Run database
run: docker-compose -f docker-compose.test.yml -f docker-compose.yml up -d db dapptools contract eth-server
- name: Test
run: |
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8081)" != "200" ]; do echo "waiting for ipld-eth-server..." && sleep 5; done && \
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8545)" != "200" ]; do echo "waiting for geth-statediff..." && sleep 5; done && \
make integrationtest
+11 -2
View File
@@ -6,8 +6,17 @@ RUN apk add busybox-extras
# Build ipld-eth-server
WORKDIR /go/src/github.com/vulcanize/ipld-eth-server
ADD . .
RUN GO111MODULE=on GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o ipld-eth-server .
# Cache the modules
ENV GO111MODULE=on
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
# Build the binary
RUN GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o ipld-eth-server .
# Copy migration tool
WORKDIR /
+4 -18
View File
@@ -61,39 +61,25 @@ test: | $(GINKGO) $(GOOSE)
dropdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) --if-exists $(TEST_DB)
createdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) $(TEST_DB)
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" up
$(GINKGO) -r --skipPackage=integration_tests,integration
$(GINKGO) -r --skipPackage=test
.PHONY: integrationtest
integrationtest: | $(GINKGO) $(GOOSE)
go vet ./...
go fmt ./...
export PGPASSWORD=$(DATABASE_PASSWORD)
dropdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) --if-exists $(TEST_DB)
createdb -h $(DATABASE_HOSTNAME) -p $(DATABASE_PORT) -U $(DATABASE_USER) $(TEST_DB)
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" up
$(GINKGO) -r integration_test/
$(GINKGO) -r test/ -v
.PHONY: test_local
test_local: | $(GINKGO) $(GOOSE)
go vet ./...
go fmt ./...
dropdb -h $(HOST_NAME) -p $(PORT) -U $(USER) --if-exists $(TEST_DB)
createdb -h $(HOST_NAME) -p $(PORT) -U $(USER) $(TEST_DB)
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" up
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" reset
make migrate NAME=$(TEST_DB)
$(GINKGO) -r --skipPackage=integration_tests,integration
./scripts/run_unit_test.sh
.PHONY: integrationtest_local
integrationtest_local: | $(GINKGO) $(GOOSE)
go vet ./...
go fmt ./...
dropdb -h $(HOST_NAME) -p $(PORT) -U $(USER) --if-exists $(TEST_DB)
createdb -h $(HOST_NAME) -p $(PORT) -U $(USER) $(TEST_DB)
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" up
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING_LOCAL)" reset
make migrate NAME=$(TEST_DB)
$(GINKGO) -r integration_test/
./scripts/run_intregration_test.sh
build:
go fmt ./...
+26
View File
@@ -121,6 +121,32 @@ The currently supported standard endpoints are:
TODO: Add the rest of the standard endpoints and unique endpoints (e.g. getSlice)
### CLI Options and Environment variables
| CLI Option | Environment Variable | Default Value | Comment |
| ----------------------------- | ----------------------------- | ---------------- | ----------------------------------- |
| `database-hostname` | `DATABASE_HOSTNAME` | localhost | IPLD database host |
| `database-port` | `DATABASE_PORT` | 5432 | IPLD database port |
| `database-name` | `DATABASE_NAME` | vulcanize_public | IPLD database name |
| `database-user` | `DATABASE_USER` | | IPLD database user |
| `database-password` | `DATABASE_PASSWORD` | | IPLD database password |
| `eth-server-graphql` | `ETH_SERVER_GRAPHQL` | false | If `true` enable Eth GraphQL Server |
| `eth-server-graphql-path` | `ETH_SERVER_GRAPHQLPATH` | | If `eth-server-graphql` set to true, endpoint url for graphql server (host:port) |
| `eth-server-http` | `ETH_SERVER_HTTP` | true | If `true` enable Eth HTTP JSON-RPC Server |
| `eth-server-http-path` | `ETH_SERVER_HTTPPATH` | | If `eth-server-http` set to `true`, endpoint url for Eth HTTP JSON-RPC server (host:port) |
| `eth-server-ws` | `ETH_SERVER_WS` | false | If `true` enable Eth WS JSON-RPC Server |
| `eth-server-ws-path` | `ETH_SERVER_WSPATH` | | If `eth-server-ws` set to `true`, endpoint url for Eth WS JSON-RPC server (host:port) |
| `eth-server-ipc` | `ETH_SERVER_IPC` | false | If `true` enable Eth IPC JSON-RPC Server |
| `eth-server-ipc-path` | `ETH_SERVER_IPC_PATH` | | If `eth-server-ws` set to `true`, path for Eth IPC JSON-RPC server |
| `ipld-server-graphql` | `IPLD_SERVER_GRAPHQL` | false | If `true` enable IPLD GraphQL Server |
| `ipld-server-graphql-path` | `IPLD_SERVER_GRAPHQLPATH` | | If `ipld-server-graphql` set to true, endpoint url for graphql server (host:port) |
| `ipld-postgraphile-path` | `IPLD_POSTGRAPHILEPATH` | | If `ipld-server-graphql` set to true, http url for postgraphile server on top of IPLD db |
| `tracing-http-path` | `TRACING_HTTPPATH` | | If `ipld-server-graphql` set to true, http url for tracing server |
| `tracing-postgraphile-path` | `TRACING.POSTGRAPHILEPATH` | | If `ipld-server-graphql` set to true, http url for postgraphile server on top of tracing db |
### Testing
`make test` will run the unit tests
`make test` setups a clean `vulcanize_testing` db
+16
View 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
View File
@@ -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"))
}
+1 -10
View File
@@ -99,11 +99,7 @@ func init() {
viper.AutomaticEnv()
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
rootCmd.PersistentFlags().Int("database-port", 5432, "database port")
rootCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname")
rootCmd.PersistentFlags().String("database-user", "", "database user")
rootCmd.PersistentFlags().String("database-password", "", "database password")
rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file")
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "log level (trace, debug, info, warn, error, fatal, panic)")
rootCmd.PersistentFlags().String("log-file", "", "file path for logging")
@@ -114,11 +110,6 @@ func init() {
rootCmd.PersistentFlags().String("prom-http-addr", "127.0.0.1", "http host for prometheus")
rootCmd.PersistentFlags().String("prom-http-port", "8090", "http port for prometheus")
viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name"))
viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port"))
viper.BindPFlag("database.hostname", rootCmd.PersistentFlags().Lookup("database-hostname"))
viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user"))
viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password"))
viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
viper.BindPFlag("log.file", rootCmd.PersistentFlags().Lookup("log-file"))
+175 -31
View File
@@ -16,24 +16,29 @@
package cmd
import (
"errors"
"net/http"
"net/url"
"os"
"os/signal"
"strings"
"sync"
"github.com/vulcanize/ipld-eth-server/pkg/graphql"
"github.com/ethereum/go-ethereum/rpc"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/vulcanize/gap-filler/pkg/mux"
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
"github.com/vulcanize/ipld-eth-server/pkg/graphql"
srpc "github.com/vulcanize/ipld-eth-server/pkg/rpc"
s "github.com/vulcanize/ipld-eth-server/pkg/serve"
v "github.com/vulcanize/ipld-eth-server/version"
)
var ErrNoRpcEndpoints = errors.New("no rpc endpoints is available")
// serveCmd represents the serve command
var serveCmd = &cobra.Command{
Use: "serve",
@@ -71,7 +76,12 @@ func serve() {
if err := startServers(server, serverConfig); err != nil {
logWithCommand.Fatal(err)
}
graphQL, err := startGraphQL(server)
graphQL, err := startEthGraphQL(server, serverConfig)
if err != nil {
logWithCommand.Fatal(err)
}
err = startIpldGraphQL(serverConfig)
if err != nil {
logWithCommand.Fatal(err)
}
@@ -87,27 +97,43 @@ func serve() {
}
func startServers(server s.Server, settings *s.Config) error {
logWithCommand.Info("starting up IPC server")
_, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
if err != nil {
return err
if settings.IPCEnabled {
logWithCommand.Info("starting up IPC server")
_, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
if err != nil {
return err
}
} else {
logWithCommand.Info("IPC server is disabled")
}
logWithCommand.Info("starting up WS server")
_, _, err = srpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb"}, nil, true)
if err != nil {
return err
if settings.WSEnabled {
logWithCommand.Info("starting up WS server")
_, _, err := srpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb", "net"}, nil, true)
if err != nil {
return err
}
} else {
logWithCommand.Info("WS server is disabled")
}
logWithCommand.Info("starting up HTTP server")
_, err = srpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"eth", "net"}, nil, []string{"*"}, rpc.HTTPTimeouts{})
return err
if settings.HTTPEnabled {
logWithCommand.Info("starting up HTTP server")
_, err := srpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"eth", "net"}, nil, []string{"*"}, rpc.HTTPTimeouts{})
if err != nil {
return err
}
} else {
logWithCommand.Info("HTTP server is disabled")
}
return nil
}
func startGraphQL(server s.Server) (graphQLServer *graphql.Service, err error) {
viper.BindEnv("server.graphql", "SERVER_GRAPHQL")
if viper.GetBool("server.graphql") {
logWithCommand.Info("starting up GraphQL server")
viper.BindEnv("server.graphqlEndpoint", "SERVER_GRAPHQL_ENDPOINT")
endPoint := viper.GetString("server.graphqlEndpoint")
func startEthGraphQL(server s.Server, settings *s.Config) (graphQLServer *graphql.Service, err error) {
if settings.EthGraphqlEnabled {
logWithCommand.Info("starting up ETH GraphQL server")
endPoint := settings.EthGraphqlEndpoint
if endPoint != "" {
graphQLServer, err = graphql.New(server.Backend(), endPoint, nil, []string{"*"}, rpc.HTTPTimeouts{})
if err != nil {
@@ -115,19 +141,113 @@ func startGraphQL(server s.Server) (graphQLServer *graphql.Service, err error) {
}
err = graphQLServer.Start(nil)
}
} else {
logWithCommand.Info("ETH GraphQL server is disabled")
}
return
}
func startIpldGraphQL(settings *s.Config) error {
if settings.IpldGraphqlEnabled {
logWithCommand.Info("starting up IPLD GraphQL server")
gqlIpldAddr, err := url.Parse(settings.IpldPostgraphileEndpoint)
if err != nil {
return err
}
gqlTracingAPIAddr, err := url.Parse(settings.TracingPostgraphileEndpoint)
if err != nil {
return err
}
ethClients, err := parseRpcAddresses(settings.EthHttpEndpoint)
if err != nil {
return err
}
var tracingClients []*rpc.Client
tracingEndpoint := viper.GetString("tracing.httpPath")
if tracingEndpoint != "" {
tracingClients, err = parseRpcAddresses(tracingEndpoint)
if err != nil {
return err
}
}
router, err := mux.NewServeMux(&mux.Options{
BasePath: "/",
EnableGraphiQL: true,
Postgraphile: mux.PostgraphileOptions{
Default: gqlIpldAddr,
TracingAPI: gqlTracingAPIAddr,
},
RPC: mux.RPCOptions{
DefaultClients: ethClients,
TracingClients: tracingClients,
},
})
if err != nil {
return err
}
go http.ListenAndServe(settings.IpldGraphqlEndpoint, router)
} else {
logWithCommand.Info("IPLD GraphQL server is disabled")
}
return nil
}
func parseRpcAddresses(value string) ([]*rpc.Client, error) {
rpcAddresses := strings.Split(value, ",")
rpcClients := make([]*rpc.Client, 0, len(rpcAddresses))
for _, address := range rpcAddresses {
rpcClient, err := rpc.Dial(address)
if err != nil {
logWithCommand.Errorf("couldn't connect to %s. Error: %s", address, err)
continue
}
rpcClients = append(rpcClients, rpcClient)
}
if len(rpcClients) == 0 {
logWithCommand.Error(ErrNoRpcEndpoints)
return nil, ErrNoRpcEndpoints
}
return rpcClients, nil
}
func init() {
rootCmd.AddCommand(serveCmd)
// database credentials
serveCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
serveCmd.PersistentFlags().Int("database-port", 5432, "database port")
serveCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname")
serveCmd.PersistentFlags().String("database-user", "", "database user")
serveCmd.PersistentFlags().String("database-password", "", "database password")
// flags for all config variables
serveCmd.PersistentFlags().Bool("server-graphql", false, "turn on the graphql server")
serveCmd.PersistentFlags().String("server-graphql-endpoint", "", "endpoint url for graphql server")
serveCmd.PersistentFlags().String("server-ws-path", "", "vdb server ws path")
serveCmd.PersistentFlags().String("server-http-path", "", "vdb server http path")
serveCmd.PersistentFlags().String("server-ipc-path", "", "vdb server ipc path")
// eth graphql and json-rpc parameters
serveCmd.PersistentFlags().Bool("eth-server-graphql", false, "turn on the eth graphql server")
serveCmd.PersistentFlags().String("eth-server-graphql-path", "", "endpoint url for eth graphql server (host:port)")
serveCmd.PersistentFlags().Bool("eth-server-http", true, "turn on the eth http json-rpc server")
serveCmd.PersistentFlags().String("eth-server-http-path", "", "endpoint url for eth http json-rpc server (host:port)")
serveCmd.PersistentFlags().Bool("eth-server-ws", false, "turn on the eth websocket json-rpc server")
serveCmd.PersistentFlags().String("eth-server-ws-path", "", "endpoint url for eth websocket json-rpc server (host:port)")
serveCmd.PersistentFlags().Bool("eth-server-ipc", false, "turn on the eth ipc json-rpc server")
serveCmd.PersistentFlags().String("eth-server-ipc-path", "", "path for eth ipc json-rpc server")
// ipld and tracing graphql parameters
serveCmd.PersistentFlags().Bool("ipld-server-graphql", false, "turn on the ipld graphql server")
serveCmd.PersistentFlags().String("ipld-server-graphql-path", "", "endpoint url for ipld graphql server (host:port)")
serveCmd.PersistentFlags().String("ipld-postgraphile-path", "", "http url to postgraphile on top of ipld database")
serveCmd.PersistentFlags().String("tracing-http-path", "", "http url to tracing service")
serveCmd.PersistentFlags().String("tracing-postgraphile-path", "", "http url to postgraphile on top of tracing db")
serveCmd.PersistentFlags().String("eth-http-path", "", "http url for ethereum node")
serveCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
@@ -141,11 +261,35 @@ func init() {
serveCmd.PersistentFlags().Bool("eth-supports-state-diff", false, "whether or not the proxy ethereum client supports statediffing endpoints")
// and their bindings
viper.BindPFlag("server.graphql", serveCmd.PersistentFlags().Lookup("server-graphql"))
viper.BindPFlag("server.graphqlEndpoint", serveCmd.PersistentFlags().Lookup("server-graphql-endpoint"))
viper.BindPFlag("server.wsPath", serveCmd.PersistentFlags().Lookup("server-ws-path"))
viper.BindPFlag("server.httpPath", serveCmd.PersistentFlags().Lookup("server-http-path"))
viper.BindPFlag("server.ipcPath", serveCmd.PersistentFlags().Lookup("server-ipc-path"))
// database
viper.BindPFlag("database.name", serveCmd.PersistentFlags().Lookup("database-name"))
viper.BindPFlag("database.port", serveCmd.PersistentFlags().Lookup("database-port"))
viper.BindPFlag("database.hostname", serveCmd.PersistentFlags().Lookup("database-hostname"))
viper.BindPFlag("database.user", serveCmd.PersistentFlags().Lookup("database-user"))
viper.BindPFlag("database.password", serveCmd.PersistentFlags().Lookup("database-password"))
// eth graphql server
viper.BindPFlag("eth.server.graphql", serveCmd.PersistentFlags().Lookup("eth-server-graphql"))
viper.BindPFlag("eth.server.graphqlPath", serveCmd.PersistentFlags().Lookup("eth-server-graphql-path"))
// eth http json-rpc server
viper.BindPFlag("eth.server.http", serveCmd.PersistentFlags().Lookup("eth-server-http"))
viper.BindPFlag("eth.server.httpPath", serveCmd.PersistentFlags().Lookup("eth-server-http-path"))
// eth websocket json-rpc server
viper.BindPFlag("eth.server.ws", serveCmd.PersistentFlags().Lookup("eth-server-ws"))
viper.BindPFlag("eth.server.wsPath", serveCmd.PersistentFlags().Lookup("eth-server-ws-path"))
// eth ipc json-rpc server
viper.BindPFlag("eth.server.ipc", serveCmd.PersistentFlags().Lookup("eth-server-ipc"))
viper.BindPFlag("eth.server.ipcPath", serveCmd.PersistentFlags().Lookup("eth-server-ipc-path"))
// ipld and tracing graphql parameters
viper.BindPFlag("ipld.server.graphql", serveCmd.PersistentFlags().Lookup("ipld-server-graphql"))
viper.BindPFlag("ipld.server.graphqlPath", serveCmd.PersistentFlags().Lookup("ipld-server-graphql-path"))
viper.BindPFlag("ipld.postgraphilePath", serveCmd.PersistentFlags().Lookup("ipld-postgraphile-path"))
viper.BindPFlag("tracing.httpPath", serveCmd.PersistentFlags().Lookup("tracing-http-path"))
viper.BindPFlag("tracing.postgraphilePath", serveCmd.PersistentFlags().Lookup("tracing-postgraphile-path"))
viper.BindPFlag("ethereum.httpPath", serveCmd.PersistentFlags().Lookup("eth-http-path"))
viper.BindPFlag("ethereum.nodeID", serveCmd.PersistentFlags().Lookup("eth-node-id"))
@@ -1,6 +1,6 @@
-- +goose Up
-- +goose StatementBegin
-- returns if a storage node at the provided path was removed in the range > the provided height and <= the provided block hash
-- returns if a storage node at the provided path was removed in the range >= the provided height and <= the provided block hash
CREATE OR REPLACE FUNCTION was_storage_removed(path BYTEA, height BIGINT, hash VARCHAR(66)) RETURNS BOOLEAN
AS $$
SELECT exists(SELECT 1
@@ -8,7 +8,7 @@ SELECT exists(SELECT 1
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
WHERE storage_path = path
AND block_number > height
AND block_number >= height
AND block_number <= (SELECT block_number
FROM eth.header_cids
WHERE block_hash = hash)
+14
View 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"
+31 -22
View File
@@ -3,11 +3,31 @@ version: '3.2'
services:
dapptools:
restart: unless-stopped
image: vulcanize/dapptools:v0.29.0-statediff-0.0.2
depends_on:
- statediff-migrations
image: vulcanize/dapptools:v0.29.0-v1.10.2-statediff-0.0.19
environment:
DB_USER: vdbm
DB_NAME: vulcanize_public
DB_HOST: db
DB_PORT: 5432
DB_PASSWORD: password
ports:
- "127.0.0.1:8545:8545"
- "127.0.0.1:8546:8546"
statediff-migrations:
restart: on-failure
depends_on:
- db
image: vulcanize/statediff-migrations:v0.4.0
environment:
DATABASE_USER: vdbm
DATABASE_NAME: vulcanize_public
DATABASE_HOSTNAME: db
DATABASE_PORT: 5432
DATABASE_PASSWORD: password
db:
restart: always
image: postgres:10.12-alpine
@@ -20,25 +40,8 @@ services:
ports:
- "127.0.0.1:8077:5432"
eth-indexer:
restart: unless-stopped
depends_on:
- db
- dapptools
image: vulcanize/ipld-eth-indexer:v0.3.0-alpha
environment:
DATABASE_NAME: vulcanize_public
DATABASE_HOSTNAME: db
DATABASE_PORT: 5432
DATABASE_USER: vdbm
DATABASE_PASSWORD: password
ETH_WS_PATH: "dapptools:8546"
ETH_HTTP_PATH: "dapptools:8545"
ETH_CHAIN_ID: 4
ETH_NETWORK_ID: 4
VDB_COMMAND: sync
eth-server:
restart: unless-stopped
depends_on:
- db
build:
@@ -47,16 +50,22 @@ services:
- alpine:latest
- golang:1.13-alpine
environment:
IPLD_SERVER_GRAPHQL: "true"
IPLD_POSTGRAPHILEPATH: http://graphql:5000
ETH_SERVER_HTTPPATH: 0.0.0.0:8081
VDB_COMMAND: "serve"
ETH_CHAIN_CONFIG: "/tmp/chain.json"
DATABASE_NAME: "vulcanize_public"
DATABASE_HOSTNAME: "db"
DATABASE_PORT: 5432
DATABASE_USER: "vdbm"
DATABASE_PASSWORD: "password"
SERVER_WS_PATH: "0.0.0.0:8081"
SERVER_HTTP_PATH: "0.0.0.0:8082"
ETH_CHAIN_ID: 4
volumes:
- type: bind
source: ./chain.json
target: /tmp/chain.json
ports:
- "127.0.0.1:8080:8080"
- "127.0.0.1:8081:8081"
graphql:
+9 -7
View File
@@ -4,27 +4,29 @@ go 1.13
require (
github.com/ethereum/go-ethereum v1.9.25
github.com/graph-gophers/graphql-go v0.0.0-20201003130358-c5bdf3b1108e
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29
github.com/ipfs/go-block-format v0.0.2
github.com/ipfs/go-cid v0.0.7
github.com/ipfs/go-ipfs-blockstore v1.0.1
github.com/ipfs/go-ipfs-ds-help v1.0.0
github.com/ipfs/go-ipld-format v0.2.0
github.com/jmoiron/sqlx v1.2.0
github.com/lib/pq v1.8.0
github.com/lib/pq v1.10.2
github.com/machinebox/graphql v0.2.2
github.com/multiformats/go-multihash v0.0.14
github.com/onsi/ginkgo v1.15.0
github.com/onsi/ginkgo v1.16.2
github.com/onsi/gomega v1.10.1
github.com/prometheus/client_golang v1.5.1
github.com/shirou/gopsutil v3.21.5+incompatible // indirect
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.0
github.com/vulcanize/ipld-eth-indexer v0.7.1-alpha
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/vulcanize/gap-filler v0.3.1
github.com/vulcanize/ipfs-ethdb v0.0.2-alpha
golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b // indirect
github.com/vulcanize/ipld-eth-indexer v0.7.1-alpha.0.20210805022537-b4692fa49849
)
replace github.com/ethereum/go-ethereum v1.9.25 => github.com/vulcanize/go-ethereum v1.9.25-statediff-0.0.15
replace github.com/ethereum/go-ethereum v1.9.25 => github.com/vulcanize/go-ethereum v1.10.4-statediff-0.0.25
replace github.com/vulcanize/ipfs-ethdb v0.0.2-alpha => github.com/vulcanize/pg-ipfs-ethdb v0.0.2-alpha
replace github.com/vulcanize/ipfs-ethdb v0.0.2-alpha => github.com/vulcanize/pg-ipfs-ethdb v0.0.2-alpha
+505
View File
File diff suppressed because it is too large Load Diff
+141 -31
View File
@@ -18,27 +18,27 @@ package eth
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"math"
"io"
"math/big"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/statediff"
"github.com/sirupsen/logrus"
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
"github.com/vulcanize/ipld-eth-server/pkg/shared"
)
@@ -167,6 +167,21 @@ func (pea *PublicEthAPI) GetBlockByHash(ctx context.Context, hash common.Hash, f
return nil, err
}
// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config.
func (pea *PublicEthAPI) ChainId() hexutil.Uint64 {
chainID := new(big.Int)
block, err := pea.B.CurrentBlock()
if err != nil {
logrus.Errorf("ChainId failed with err %s", err.Error())
return 0
}
if config := pea.B.Config.ChainConfig; config.IsEIP155(block.Number()) {
chainID = config.ChainID
}
return (hexutil.Uint64)(chainID.Uint64())
}
/*
Uncles
@@ -377,7 +392,12 @@ func (pea *PublicEthAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Contex
func (pea *PublicEthAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
tx, blockHash, blockNumber, index, err := pea.B.GetTransaction(ctx, hash)
if tx != nil && err == nil {
return NewRPCTransaction(tx, blockHash, blockNumber, index), nil
header, err := pea.B.HeaderByHash(ctx, blockHash)
if err != nil {
return nil, err
}
return NewRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil
}
if pea.rpc != nil {
var tx *RPCTransaction
@@ -440,6 +460,14 @@ func (pea *PublicEthAPI) localGetTransactionReceipt(ctx context.Context, hash co
if err != nil {
return nil, err
}
block, err := pea.B.BlockByHash(ctx, blockHash)
if err != nil {
return nil, err
}
err = receipts.DeriveFields(pea.B.Config.ChainConfig, blockHash, blockNumber, block.Transactions())
if err != nil {
return nil, err
}
if len(receipts) <= int(index) {
return nil, nil
}
@@ -506,30 +534,30 @@ func (pea *PublicEthAPI) remoteGetTransactionReceipt(ctx context.Context, hash c
// GetLogs returns logs matching the given argument that are stored within the state.
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
func (pea *PublicEthAPI) GetLogs(ctx context.Context, crit ethereum.FilterQuery) ([]*types.Log, error) {
logs, err := pea.localGetLogs(ctx, crit)
func (pea *PublicEthAPI) GetLogs(ctx context.Context, crit filters.FilterCriteria) ([]*types.Log, error) {
logs, err := pea.localGetLogs(crit)
if err != nil && pea.rpc != nil {
if arg, err := toFilterArg(crit); err == nil {
var res []*types.Log
if err := pea.rpc.CallContext(ctx, &res, "eth_getLogs", arg); err == nil {
go pea.writeStateDiffWithCriteria(crit)
return res, nil
}
var res []*types.Log
if err := pea.rpc.CallContext(ctx, &res, "eth_getLogs", crit); err == nil {
go pea.writeStateDiffWithCriteria(crit)
return res, nil
}
}
return logs, err
}
func (pea *PublicEthAPI) localGetLogs(ctx context.Context, crit ethereum.FilterQuery) ([]*types.Log, error) {
func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log, error) {
// TODO: this can be optimized away from using the old cid retriever and ipld fetcher interfaces
// Convert FilterQuery into ReceiptFilter
addrStrs := make([]string, len(crit.Addresses))
for i, addr := range crit.Addresses {
addrStrs[i] = addr.String()
}
topicStrSets := make([][]string, 4)
topicStrSets := make([][]string, len(crit.Topics))
for i, topicSet := range crit.Topics {
if i > 3 {
topicStrSets = topicStrSets[:4]
// don't allow more than 4 topics
break
}
@@ -571,8 +599,13 @@ func (pea *PublicEthAPI) localGetLogs(ctx context.Context, crit ethereum.FilterQ
if err := tx.Commit(); err != nil {
return nil, err
}
return extractLogsOfInterest(rctIPLDs, filter.Topics)
block, err := pea.B.BlockByHash(context.Background(), *crit.BlockHash)
if err != nil {
return nil, err
}
return extractLogsOfInterest(pea.B.Config.ChainConfig, *crit.BlockHash, block.NumberU64(), block.Transactions(), rctIPLDs, filter)
}
// Otherwise, create block range from criteria
// nil values are filled in; to request a single block have both ToBlock and FromBlock equal that number
startingBlock := crit.FromBlock
@@ -580,6 +613,7 @@ func (pea *PublicEthAPI) localGetLogs(ctx context.Context, crit ethereum.FilterQ
if startingBlock == nil {
startingBlock = common.Big0
}
if endingBlock == nil {
endingBlockInt, err := pea.B.Retriever.RetrieveLastBlockNumber()
if err != nil {
@@ -587,24 +621,38 @@ func (pea *PublicEthAPI) localGetLogs(ctx context.Context, crit ethereum.FilterQ
}
endingBlock = big.NewInt(endingBlockInt)
}
start := startingBlock.Int64()
end := endingBlock.Int64()
allRctCIDs := make([]eth.ReceiptModel, 0)
var logs []*types.Log
for i := start; i <= end; i++ {
rctCIDs, err := pea.B.Retriever.RetrieveRctCIDs(tx, filter, i, nil, nil)
if err != nil {
return nil, err
}
allRctCIDs = append(allRctCIDs, rctCIDs...)
}
rctIPLDs, err := pea.B.Fetcher.FetchRcts(tx, allRctCIDs)
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
}
logs, err := extractLogsOfInterest(rctIPLDs, filter.Topics)
return logs, err // need to return err variable so that we return the err = tx.Commit() assignment in the defer
}
@@ -629,6 +677,10 @@ func (pea *PublicEthAPI) GetBalance(ctx context.Context, address common.Address,
return res, nil
}
}
if err == sql.ErrNoRows {
return (*hexutil.Big)(big.NewInt(0)), nil
}
return nil, err
}
@@ -646,7 +698,17 @@ func (pea *PublicEthAPI) localGetBalance(ctx context.Context, address common.Add
func (pea *PublicEthAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
storageVal, err := pea.B.GetStorageByNumberOrHash(ctx, address, common.HexToHash(key), blockNrOrHash)
if storageVal != nil && err == nil {
return storageVal, nil
var value common.Hash
_, content, _, err := rlp.Split(storageVal)
if err == io.ErrUnexpectedEOF {
return hexutil.Bytes{}, nil
}
if err != nil {
return nil, err
}
value.SetBytes(content)
return value[:], nil
}
if pea.rpc != nil {
var res hexutil.Bytes
@@ -655,6 +717,9 @@ func (pea *PublicEthAPI) GetStorageAt(ctx context.Context, address common.Addres
return res, nil
}
}
if err == sql.ErrNoRows {
return make([]byte, 32), nil
}
return nil, err
}
@@ -671,6 +736,10 @@ func (pea *PublicEthAPI) GetCode(ctx context.Context, address common.Address, bl
return res, nil
}
}
if err == sql.ErrNoRows {
return code, nil
}
return nil, err
}
@@ -814,13 +883,47 @@ func DoCall(ctx context.Context, b *Backend, args CallArgs, blockNrOrHash rpc.Bl
if args.Gas != nil {
gas = uint64(*args.Gas)
}
if globalGasCap != nil && globalGasCap.Uint64() < gas {
logrus.Warnf("Caller gas above allowance, capping; requested: %d, cap: %d", gas, globalGasCap)
gas = globalGasCap.Uint64()
}
gasPrice := new(big.Int).SetUint64(params.GWei)
if args.GasPrice != nil {
gasPrice = args.GasPrice.ToInt()
var (
gasPrice *big.Int
gasFeeCap *big.Int
gasTipCap *big.Int
)
if header.BaseFee == nil {
// If there's no basefee, then it must be a non-1559 execution
gasPrice = new(big.Int)
if args.GasPrice != nil {
gasPrice = args.GasPrice.ToInt()
}
gasFeeCap, gasTipCap = gasPrice, gasPrice
} else {
// A basefee is provided, necessitating 1559-type execution
if args.GasPrice != nil {
// User specified the legacy gas field, convert to 1559 gas typing
gasPrice = args.GasPrice.ToInt()
gasFeeCap, gasTipCap = gasPrice, gasPrice
} else {
// User specified 1559 gas feilds (or none), use those
gasFeeCap = new(big.Int)
if args.MaxFeePerGas != nil {
gasFeeCap = args.MaxFeePerGas.ToInt()
}
gasTipCap = new(big.Int)
if args.MaxPriorityFeePerGas != nil {
gasTipCap = args.MaxPriorityFeePerGas.ToInt()
}
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
gasPrice = new(big.Int)
if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 {
gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, header.BaseFee), gasFeeCap)
}
}
}
value := new(big.Int)
@@ -829,12 +932,19 @@ func DoCall(ctx context.Context, b *Backend, args CallArgs, blockNrOrHash rpc.Bl
}
var data []byte
if args.Data != nil {
data = []byte(*args.Data)
if args.Input != nil {
data = *args.Input
} else if args.Data != nil {
data = *args.Data
}
var accessList types.AccessList
if args.AccessList != nil {
accessList = *args.AccessList
}
// Create new call message
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false)
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, false)
// Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout.
@@ -890,7 +1000,7 @@ func (pea *PublicEthAPI) writeStateDiffAtOrFor(blockNrOrHash rpc.BlockNumberOrHa
}
// writeStateDiffWithCriteria calls out to the proxy statediffing geth client to fill in a gap in the index
func (pea *PublicEthAPI) writeStateDiffWithCriteria(crit ethereum.FilterQuery) {
func (pea *PublicEthAPI) writeStateDiffWithCriteria(crit filters.FilterCriteria) {
// short circuit right away if the proxy doesn't support diffing
if !pea.supportsStateDiff {
return
+127 -64
View File
@@ -18,13 +18,16 @@ package eth_test
import (
"context"
"math/big"
"strconv"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
. "github.com/onsi/ginkgo"
@@ -39,13 +42,16 @@ import (
)
var (
randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
randomHash = crypto.Keccak256Hash(randomAddr.Bytes())
number = rpc.BlockNumber(test_helpers.BlockNumber.Int64())
wrongNumber = rpc.BlockNumber(number + 1)
blockHash = test_helpers.MockBlock.Header().Hash()
ctx = context.Background()
expectedBlock = map[string]interface{}{
randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
randomHash = crypto.Keccak256Hash(randomAddr.Bytes())
number = rpc.BlockNumber(test_helpers.BlockNumber.Int64())
londonBlockNum = rpc.BlockNumber(test_helpers.LondonBlockNum.Int64())
wrongNumber = rpc.BlockNumber(number + 1)
blockHash = test_helpers.MockBlock.Header().Hash()
londonBlockHash = test_helpers.MockLondonBlock.Header().Hash()
baseFee = test_helpers.MockLondonBlock.BaseFee()
ctx = context.Background()
expectedBlock = map[string]interface{}{
"number": (*hexutil.Big)(test_helpers.MockBlock.Number()),
"hash": test_helpers.MockBlock.Hash(),
"parentHash": test_helpers.MockBlock.ParentHash(),
@@ -125,13 +131,14 @@ var (
"receiptsRoot": test_helpers.MockUncles[1].ReceiptHash,
"uncles": []common.Hash{},
}
expectedTransaction = eth.NewRPCTransaction(test_helpers.MockTransactions[0], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 0)
expectedTransaction2 = eth.NewRPCTransaction(test_helpers.MockTransactions[1], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 1)
expectedTransaction3 = eth.NewRPCTransaction(test_helpers.MockTransactions[2], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 2)
expectRawTx, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[0])
expectRawTx2, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[1])
expectRawTx3, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[2])
expectedReceipt = map[string]interface{}{
expectedTransaction = eth.NewRPCTransaction(test_helpers.MockTransactions[0], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 0, test_helpers.MockBlock.BaseFee())
expectedTransaction2 = eth.NewRPCTransaction(test_helpers.MockTransactions[1], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 1, test_helpers.MockBlock.BaseFee())
expectedTransaction3 = eth.NewRPCTransaction(test_helpers.MockTransactions[2], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 2, test_helpers.MockBlock.BaseFee())
expectedLondonTransaction = eth.NewRPCTransaction(test_helpers.MockLondonTransactions[0], test_helpers.MockLondonBlock.Hash(), test_helpers.MockLondonBlock.NumberU64(), 0, test_helpers.MockLondonBlock.BaseFee())
expectRawTx, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[0])
expectRawTx2, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[1])
expectRawTx3, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[2])
expectedReceipt = map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(uint64(number.Int64())),
"transactionHash": expectedTransaction.Hash,
@@ -168,7 +175,7 @@ var (
"to": expectedTransaction3.To,
"gasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].GasUsed),
"cumulativeGasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].CumulativeGasUsed),
"contractAddress": nil,
"contractAddress": test_helpers.ContractAddress,
"logs": test_helpers.MockReceipts[2].Logs,
"logsBloom": test_helpers.MockReceipts[2].Bloom,
"root": hexutil.Bytes(test_helpers.MockReceipts[2].PostState),
@@ -177,8 +184,9 @@ var (
var _ = Describe("API", func() {
var (
db *postgres.DB
api *eth.PublicEthAPI
db *postgres.DB
api *eth.PublicEthAPI
chainConfig = params.TestChainConfig
)
// Test db setup, rather than using BeforeEach we only need to setup once since the tests do not mutate the database
// Note: if you focus one of the tests be sure to focus this and the defered It()
@@ -187,7 +195,11 @@ var _ = Describe("API", func() {
db, err = shared.SetupDB()
Expect(err).ToNot(HaveOccurred())
indexAndPublisher := eth2.NewIPLDPublisher(db)
backend, err := eth.NewEthBackend(db, &eth.Config{})
backend, err := eth.NewEthBackend(db, &eth.Config{
ChainConfig: chainConfig,
VmConfig: vm.Config{},
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
})
Expect(err).ToNot(HaveOccurred())
api = eth.NewPublicEthAPI(backend, nil, false)
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
@@ -200,7 +212,11 @@ var _ = Describe("API", func() {
uncleHashes[i] = uncle.Hash()
}
expectedBlock["uncles"] = uncleHashes
err = indexAndPublisher.Publish(test_helpers.MockConvertedLondonPayload)
Expect(err).ToNot(HaveOccurred())
})
// Single test db tear down at end of all tests
defer It("test teardown", func() { eth.TearDownDB(db) })
/*
@@ -242,7 +258,7 @@ var _ = Describe("API", func() {
bn := api.BlockNumber()
ubn := (uint64)(bn)
subn := strconv.FormatUint(ubn, 10)
Expect(subn).To(Equal(test_helpers.BlockNumber.String()))
Expect(subn).To(Equal(test_helpers.LondonBlockNum.String()))
})
})
@@ -271,10 +287,20 @@ var _ = Describe("API", func() {
Expect(val).To(Equal(block[key]))
}
})
It("Throws an error if a block cannot be found", func() {
_, err := api.GetBlockByNumber(ctx, wrongNumber, false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns `nil` if a block cannot be found", func() {
block, err := api.GetBlockByNumber(ctx, wrongNumber, false)
Expect(err).ToNot(HaveOccurred())
Expect(block).To(BeNil())
})
It("Fetch BaseFee from london block by block number, returns `nil` for legacy block", func() {
block, err := api.GetBlockByNumber(ctx, number, false)
Expect(err).ToNot(HaveOccurred())
_, ok := block["baseFee"]
Expect(ok).To(Equal(false))
block, err = api.GetBlockByNumber(ctx, londonBlockNum, false)
Expect(err).ToNot(HaveOccurred())
Expect(block["baseFee"].(*big.Int)).To(Equal(baseFee))
})
})
@@ -303,10 +329,20 @@ var _ = Describe("API", func() {
Expect(val).To(Equal(block[key]))
}
})
It("Throws an error if a block cannot be found", func() {
_, err := api.GetBlockByHash(ctx, randomHash, false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns `nil` if a block cannot be found", func() {
block, err := api.GetBlockByHash(ctx, randomHash, false)
Expect(err).ToNot(HaveOccurred())
Expect(block).To(BeZero())
})
It("Fetch BaseFee from london block by block hash, returns `nil` for legacy block", func() {
block, err := api.GetBlockByHash(ctx, test_helpers.MockBlock.Hash(), true)
Expect(err).ToNot(HaveOccurred())
_, ok := block["baseFee"]
Expect(ok).To(Equal(false))
block, err = api.GetBlockByHash(ctx, londonBlockHash, false)
Expect(err).ToNot(HaveOccurred())
Expect(block["baseFee"].(*big.Int)).To(Equal(baseFee))
})
})
@@ -325,10 +361,10 @@ var _ = Describe("API", func() {
Expect(err).ToNot(HaveOccurred())
Expect(uncle2).To(Equal(expectedUncle2))
})
It("Throws an error if an block for blocknumber cannot be found", func() {
_, err := api.GetUncleByBlockNumberAndIndex(ctx, wrongNumber, 0)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns `nil` if an block for block number cannot be found", func() {
block, err := api.GetUncleByBlockNumberAndIndex(ctx, wrongNumber, 0)
Expect(err).ToNot(HaveOccurred())
Expect(block).To(BeNil())
})
It("Returns `nil` if an uncle at the provided index does not exist for the block found for the provided block number", func() {
uncle, err := api.GetUncleByBlockNumberAndIndex(ctx, number, 2)
@@ -346,10 +382,10 @@ var _ = Describe("API", func() {
Expect(err).ToNot(HaveOccurred())
Expect(uncle2).To(Equal(expectedUncle2))
})
It("Throws an error if an block for blockhash cannot be found", func() {
_, err := api.GetUncleByBlockHashAndIndex(ctx, randomHash, 0)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns `nil` if a block for blockhash cannot be found", func() {
block, err := api.GetUncleByBlockHashAndIndex(ctx, randomHash, 0)
Expect(err).ToNot(HaveOccurred())
Expect(block).To(BeNil())
})
It("Returns `nil` if an uncle at the provided index does not exist for the block with the provided hash", func() {
uncle, err := api.GetUncleByBlockHashAndIndex(ctx, blockHash, 2)
@@ -361,6 +397,7 @@ var _ = Describe("API", func() {
Describe("eth_getUncleCountByBlockNumber", func() {
It("Retrieves the number of uncles for the canonical block with the provided number", func() {
count := api.GetUncleCountByBlockNumber(ctx, number)
Expect(*count).NotTo(Equal(nil))
Expect(uint64(*count)).To(Equal(uint64(2)))
})
})
@@ -368,6 +405,7 @@ var _ = Describe("API", func() {
Describe("eth_getUncleCountByBlockHash", func() {
It("Retrieves the number of uncles for the block with the provided hash", func() {
count := api.GetUncleCountByBlockHash(ctx, blockHash)
Expect(*count).NotTo(Equal(nil))
Expect(uint64(*count)).To(Equal(uint64(2)))
})
})
@@ -427,6 +465,13 @@ var _ = Describe("API", func() {
Expect(tx).ToNot(BeNil())
Expect(tx).To(Equal(expectedTransaction3))
})
It("Retrieves the GasFeeCap and GasTipCap for dynamic transaction from the london block hash", func() {
tx := api.GetTransactionByBlockNumberAndIndex(ctx, londonBlockNum, 0)
Expect(tx).ToNot(BeNil())
Expect(tx.GasFeeCap).To(Equal((*hexutil.Big)(test_helpers.MockLondonTransactions[0].GasFeeCap())))
Expect(tx.GasTipCap).To(Equal((*hexutil.Big)(test_helpers.MockLondonTransactions[0].GasTipCap())))
Expect(tx).To(Equal(expectedLondonTransaction))
})
})
Describe("eth_getTransactionByBlockHashAndIndex", func() {
@@ -443,6 +488,14 @@ var _ = Describe("API", func() {
Expect(tx).ToNot(BeNil())
Expect(tx).To(Equal(expectedTransaction3))
})
It("Retrieves the GasFeeCap and GasTipCap for dynamic transaction from the london block hash", func() {
tx := api.GetTransactionByBlockHashAndIndex(ctx, londonBlockHash, 0)
Expect(tx).ToNot(BeNil())
Expect(tx.GasFeeCap).To(Equal((*hexutil.Big)(test_helpers.MockLondonTransactions[0].GasFeeCap())))
Expect(tx.GasTipCap).To(Equal((*hexutil.Big)(test_helpers.MockLondonTransactions[0].GasTipCap())))
Expect(tx).To(Equal(expectedLondonTransaction))
})
})
Describe("eth_getRawTransactionByBlockNumberAndIndex", func() {
@@ -554,7 +607,7 @@ var _ = Describe("API", func() {
Describe("eth_getLogs", func() {
It("Retrieves receipt logs that match the provided topics within the provided range", func() {
crit := ethereum.FilterQuery{
crit := filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x04"),
@@ -568,7 +621,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x04"),
@@ -583,7 +636,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(2))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x04"),
@@ -598,7 +651,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x04"),
@@ -614,7 +667,7 @@ var _ = Describe("API", func() {
Expect(err).ToNot(HaveOccurred())
Expect(len(logs)).To(Equal(0))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x04"),
@@ -631,7 +684,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x05"),
@@ -648,7 +701,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x05"),
@@ -666,7 +719,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{
common.HexToHash("0x04"),
@@ -685,7 +738,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(2))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{},
{
@@ -700,7 +753,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{
{},
{
@@ -715,7 +768,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
Topics: [][]common.Hash{},
FromBlock: test_helpers.MockBlock.Number(),
ToBlock: test_helpers.MockBlock.Number(),
@@ -728,7 +781,7 @@ var _ = Describe("API", func() {
It("Uses the provided blockhash if one is provided", func() {
hash := test_helpers.MockBlock.Hash()
crit := ethereum.FilterQuery{
crit := filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{},
@@ -742,7 +795,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{
@@ -758,7 +811,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{},
@@ -772,7 +825,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{
@@ -788,7 +841,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{
@@ -803,7 +856,7 @@ var _ = Describe("API", func() {
Expect(err).ToNot(HaveOccurred())
Expect(len(logs)).To(Equal(0))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{
@@ -820,7 +873,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(1))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{
@@ -834,7 +887,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(2))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{
{
@@ -852,7 +905,7 @@ var _ = Describe("API", func() {
Expect(len(logs)).To(Equal(2))
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Topics: [][]common.Hash{},
}
@@ -864,7 +917,7 @@ var _ = Describe("API", func() {
It("Filters on contract address if any are provided", func() {
hash := test_helpers.MockBlock.Hash()
crit := ethereum.FilterQuery{
crit := filters.FilterCriteria{
BlockHash: &hash,
Addresses: []common.Address{
test_helpers.Address,
@@ -886,7 +939,7 @@ var _ = Describe("API", func() {
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
hash = test_helpers.MockBlock.Hash()
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Addresses: []common.Address{
test_helpers.Address,
@@ -909,7 +962,7 @@ var _ = Describe("API", func() {
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
hash = test_helpers.MockBlock.Hash()
crit = ethereum.FilterQuery{
crit = filters.FilterCriteria{
BlockHash: &hash,
Addresses: []common.Address{
test_helpers.Address,
@@ -948,8 +1001,17 @@ var _ = Describe("API", func() {
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
})
It("Throws an error for an account it cannot find the balance for", func() {
_, err := api.GetBalance(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
It("Retrieves the eth balance for the non-existing account address at the block with the provided hash", func() {
bal, err := api.GetBalance(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
})
It("Throws an error for an account of a non-existing block hash", func() {
_, err := api.GetBalance(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithHash(randomHash, true))
Expect(err).To(HaveOccurred())
})
It("Throws an error for an account of a non-existing block number", func() {
_, err := api.GetBalance(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithNumber(wrongNumber))
Expect(err).To(HaveOccurred())
})
})
@@ -965,9 +1027,10 @@ var _ = Describe("API", func() {
Expect(err).ToNot(HaveOccurred())
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
})
It("Throws an error for an account it cannot find the code for", func() {
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
Expect(err).To(HaveOccurred())
It("Returns `nil` for an account it cannot find the code for", func() {
code, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
Expect(err).ToNot(HaveOccurred())
Expect(code).To(BeEmpty())
})
})
})
+190 -51
View File
@@ -18,6 +18,7 @@ package eth
import (
"context"
"database/sql"
"errors"
"fmt"
"math/big"
@@ -40,7 +41,8 @@ import (
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
"github.com/vulcanize/ipfs-ethdb"
ipfsethdb "github.com/vulcanize/ipfs-ethdb"
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
shared2 "github.com/vulcanize/ipld-eth-indexer/pkg/shared"
@@ -50,6 +52,8 @@ import (
var (
errPendingBlockNumber = errors.New("pending block number not supported")
errNegativeBlockNumber = errors.New("negative block number not supported")
errHeaderHashNotFound = errors.New("header for hash not found")
errHeaderNotFound = errors.New("header not found")
)
const (
@@ -174,7 +178,11 @@ func (b *Backend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Bl
if header == nil {
return nil, errors.New("header for hash not found")
}
if blockNrOrHash.RequireCanonical && b.GetCanonicalHash(header.Number.Uint64()) != hash {
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
if err != nil {
return nil, err
}
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
return nil, errors.New("hash is not currently canonical")
}
return header, nil
@@ -197,9 +205,9 @@ func (b *Backend) GetTd(blockHash common.Hash) (*big.Int, error) {
}
// CurrentBlock returns the current block
func (b *Backend) CurrentBlock() *types.Block {
block, _ := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber)
return block
func (b *Backend) CurrentBlock() (*types.Block, error) {
block, err := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber)
return block, err
}
// BlockByNumberOrHash returns block by number or hash
@@ -215,7 +223,11 @@ func (b *Backend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Blo
if header == nil {
return nil, errors.New("header for hash not found")
}
if blockNrOrHash.RequireCanonical && b.GetCanonicalHash(header.Number.Uint64()) != hash {
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
if err != nil {
return nil, err
}
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
return nil, errors.New("hash is not currently canonical")
}
block, err := b.BlockByHash(ctx, hash)
@@ -253,14 +265,20 @@ func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber
return nil, errNegativeBlockNumber
}
// Get the canonical hash
canonicalHash := b.GetCanonicalHash(uint64(number))
canonicalHash, err := b.GetCanonicalHash(uint64(number))
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
// Retrieve all the CIDs for the block
// TODO: optimize this by retrieving iplds directly rather than the cids first (this is remanent from when we fetched iplds through ipfs blockservice interface)
headerCID, uncleCIDs, txCIDs, rctCIDs, err := b.Retriever.RetrieveBlockByHash(canonicalHash)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
@@ -281,49 +299,60 @@ func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber
}()
// Fetch and decode the header IPLD
headerIPLD, err := b.Fetcher.FetchHeader(tx, headerCID)
var headerIPLD ipfs.BlockModel
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
var header types.Header
if err := rlp.DecodeBytes(headerIPLD.Data, &header); err != nil {
err = rlp.DecodeBytes(headerIPLD.Data, &header)
if err != nil {
return nil, err
}
// Fetch and decode the uncle IPLDs
uncleIPLDs, err := b.Fetcher.FetchUncles(tx, uncleCIDs)
var uncleIPLDs []ipfs.BlockModel
uncleIPLDs, err = b.Fetcher.FetchUncles(tx, uncleCIDs)
if err != nil {
return nil, err
}
var uncles []*types.Header
for _, uncleIPLD := range uncleIPLDs {
var uncle types.Header
if err := rlp.DecodeBytes(uncleIPLD.Data, &uncle); err != nil {
err = rlp.DecodeBytes(uncleIPLD.Data, &uncle)
if err != nil {
return nil, err
}
uncles = append(uncles, &uncle)
}
// Fetch and decode the transaction IPLDs
txIPLDs, err := b.Fetcher.FetchTrxs(tx, txCIDs)
var txIPLDs []ipfs.BlockModel
txIPLDs, err = b.Fetcher.FetchTrxs(tx, txCIDs)
if err != nil {
return nil, err
}
var transactions []*types.Transaction
for _, txIPLD := range txIPLDs {
var transaction types.Transaction
if err := rlp.DecodeBytes(txIPLD.Data, &transaction); err != nil {
err = transaction.UnmarshalBinary(txIPLD.Data)
if err != nil {
return nil, err
}
transactions = append(transactions, &transaction)
}
// Fetch and decode the receipt IPLDs
rctIPLDs, err := b.Fetcher.FetchRcts(tx, rctCIDs)
var rctIPLDs []ipfs.BlockModel
rctIPLDs, err = b.Fetcher.FetchRcts(tx, rctCIDs)
if err != nil {
return nil, err
}
var receipts []*types.Receipt
for _, rctIPLD := range rctIPLDs {
var receipt types.Receipt
if err := rlp.DecodeBytes(rctIPLD.Data, &receipt); err != nil {
err = receipt.UnmarshalBinary(rctIPLD.Data)
if err != nil {
return nil, err
}
receipts = append(receipts, &receipt)
@@ -338,6 +367,9 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
// Retrieve all the CIDs for the block
headerCID, uncleCIDs, txCIDs, rctCIDs, err := b.Retriever.RetrieveBlockByHash(hash)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
@@ -358,49 +390,69 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
}()
// Fetch and decode the header IPLD
headerIPLD, err := b.Fetcher.FetchHeader(tx, headerCID)
var headerIPLD ipfs.BlockModel
headerIPLD, err = b.Fetcher.FetchHeader(tx, headerCID)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
var header types.Header
if err := rlp.DecodeBytes(headerIPLD.Data, &header); err != nil {
err = rlp.DecodeBytes(headerIPLD.Data, &header)
if err != nil {
return nil, err
}
// Fetch and decode the uncle IPLDs
uncleIPLDs, err := b.Fetcher.FetchUncles(tx, uncleCIDs)
var uncleIPLDs []ipfs.BlockModel
uncleIPLDs, err = b.Fetcher.FetchUncles(tx, uncleCIDs)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
var uncles []*types.Header
for _, uncleIPLD := range uncleIPLDs {
var uncle types.Header
if err := rlp.DecodeBytes(uncleIPLD.Data, &uncle); err != nil {
err = rlp.DecodeBytes(uncleIPLD.Data, &uncle)
if err != nil {
return nil, err
}
uncles = append(uncles, &uncle)
}
// Fetch and decode the transaction IPLDs
txIPLDs, err := b.Fetcher.FetchTrxs(tx, txCIDs)
var txIPLDs []ipfs.BlockModel
txIPLDs, err = b.Fetcher.FetchTrxs(tx, txCIDs)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
var transactions []*types.Transaction
for _, txIPLD := range txIPLDs {
var transaction types.Transaction
if err := rlp.DecodeBytes(txIPLD.Data, &transaction); err != nil {
err = transaction.UnmarshalBinary(txIPLD.Data)
if err != nil {
return nil, err
}
transactions = append(transactions, &transaction)
}
// Fetch and decode the receipt IPLDs
rctIPLDs, err := b.Fetcher.FetchRcts(tx, rctCIDs)
var rctIPLDs []ipfs.BlockModel
rctIPLDs, err = b.Fetcher.FetchRcts(tx, rctCIDs)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
var receipts []*types.Receipt
for _, rctIPLD := range rctIPLDs {
var receipt types.Receipt
if err := rlp.DecodeBytes(rctIPLD.Data, &receipt); err != nil {
err = receipt.UnmarshalBinary(rctIPLD.Data)
if err != nil {
return nil, err
}
receipts = append(receipts, &receipt)
@@ -422,7 +474,7 @@ func (b *Backend) GetTransaction(ctx context.Context, txHash common.Hash) (*type
return nil, common.Hash{}, 0, 0, err
}
var transaction types.Transaction
if err := rlp.DecodeBytes(tempTxStruct.Data, &transaction); err != nil {
if err := transaction.UnmarshalBinary(tempTxStruct.Data); err != nil {
return nil, common.Hash{}, 0, 0, err
}
return &transaction, common.HexToHash(tempTxStruct.BlockHash), tempTxStruct.BlockNumber, tempTxStruct.Index, nil
@@ -430,16 +482,17 @@ func (b *Backend) GetTransaction(ctx context.Context, txHash common.Hash) (*type
// GetReceipts retrieves receipts for provided block hash
func (b *Backend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
_, receiptBytes, err := b.IPLDRetriever.RetrieveReceiptsByBlockHash(hash)
_, receiptBytes, txs, err := b.IPLDRetriever.RetrieveReceiptsByBlockHash(hash)
if err != nil {
return nil, err
}
rcts := make(types.Receipts, len(receiptBytes))
for i, rctBytes := range receiptBytes {
rct := new(types.Receipt)
if err := rlp.DecodeBytes(rctBytes, rct); err != nil {
if err := rct.UnmarshalBinary(rctBytes); err != nil {
return nil, err
}
rct.TxHash = txs[i]
rcts[i] = rct
}
return rcts, nil
@@ -447,7 +500,7 @@ func (b *Backend) GetReceipts(ctx context.Context, hash common.Hash) (types.Rece
// GetLogs returns all the logs for the given block hash
func (b *Backend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
_, receiptBytes, err := b.IPLDRetriever.RetrieveReceiptsByBlockHash(hash)
_, receiptBytes, txs, err := b.IPLDRetriever.RetrieveReceiptsByBlockHash(hash)
if err != nil {
return nil, err
}
@@ -457,6 +510,11 @@ func (b *Backend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log
if err := rlp.DecodeBytes(rctBytes, &rct); err != nil {
return nil, err
}
for _, log := range rct.Logs {
log.TxHash = txs[i]
}
logs[i] = rct.Logs
}
return logs, nil
@@ -475,7 +533,11 @@ func (b *Backend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHas
if header == nil {
return nil, nil, errors.New("header for hash not found")
}
if blockNrOrHash.RequireCanonical && b.GetCanonicalHash(header.Number.Uint64()) != hash {
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
if err != nil {
return nil, nil, err
}
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
return nil, nil, errors.New("hash is not currently canonical")
}
stateDb, err := state.New(header.Root, b.StateDatabase, nil)
@@ -503,12 +565,12 @@ func (b *Backend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNu
}
// GetCanonicalHash gets the canonical hash for the provided number, if there is one
func (b *Backend) GetCanonicalHash(number uint64) common.Hash {
func (b *Backend) GetCanonicalHash(number uint64) (common.Hash, error) {
var hashResult string
if err := b.DB.Get(&hashResult, RetrieveCanonicalBlockHashByNumber, number); err != nil {
return common.Hash{}
return common.Hash{}, err
}
return common.HexToHash(hashResult)
return common.HexToHash(hashResult), nil
}
type rowResult struct {
@@ -533,7 +595,7 @@ func (b *Backend) GetEVM(ctx context.Context, msg core.Message, state *state.Sta
// GetAccountByNumberOrHash returns the account object for the provided address at the block corresponding to the provided number or hash
func (b *Backend) GetAccountByNumberOrHash(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*state.Account, error) {
if blockNr, ok := blockNrOrHash.Number(); ok {
return b.GetAccountByNumber(ctx, address, uint64(blockNr.Int64()))
return b.GetAccountByNumber(ctx, address, blockNr)
}
if hash, ok := blockNrOrHash.Hash(); ok {
return b.GetAccountByHash(ctx, address, hash)
@@ -542,20 +604,48 @@ func (b *Backend) GetAccountByNumberOrHash(ctx context.Context, address common.A
}
// GetAccountByNumber returns the account object for the provided address at the canonical block at the provided height
func (b *Backend) GetAccountByNumber(ctx context.Context, address common.Address, number uint64) (*state.Account, error) {
hash := b.GetCanonicalHash(number)
if hash == (common.Hash{}) {
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
func (b *Backend) GetAccountByNumber(ctx context.Context, address common.Address, blockNumber rpc.BlockNumber) (*state.Account, error) {
var err error
number := blockNumber.Int64()
if blockNumber == rpc.LatestBlockNumber {
number, err = b.Retriever.RetrieveLastBlockNumber()
if err != nil {
return nil, err
}
}
if blockNumber == rpc.EarliestBlockNumber {
number, err = b.Retriever.RetrieveFirstBlockNumber()
if err != nil {
return nil, err
}
}
if blockNumber == rpc.PendingBlockNumber {
return nil, errPendingBlockNumber
}
hash, err := b.GetCanonicalHash(uint64(number))
if err == sql.ErrNoRows {
return nil, errHeaderNotFound
} else if err != nil {
return nil, err
}
return b.GetAccountByHash(ctx, address, hash)
}
// GetAccountByHash returns the account object for the provided address at the block with the provided hash
func (b *Backend) GetAccountByHash(ctx context.Context, address common.Address, hash common.Hash) (*state.Account, error) {
_, err := b.HeaderByHash(context.Background(), hash)
if err == sql.ErrNoRows {
return nil, errHeaderHashNotFound
} else if err != nil {
return nil, err
}
_, accountRlp, err := b.IPLDRetriever.RetrieveAccountByAddressAndBlockHash(address, hash)
if err != nil {
return nil, err
}
acct := new(state.Account)
return acct, rlp.DecodeBytes(accountRlp, acct)
}
@@ -563,7 +653,7 @@ func (b *Backend) GetAccountByHash(ctx context.Context, address common.Address,
// GetCodeByNumberOrHash returns the byte code for the contract deployed at the provided address at the block with the provided hash or block number
func (b *Backend) GetCodeByNumberOrHash(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) ([]byte, error) {
if blockNr, ok := blockNrOrHash.Number(); ok {
return b.GetCodeByNumber(ctx, address, uint64(blockNr.Int64()))
return b.GetCodeByNumber(ctx, address, blockNr)
}
if hash, ok := blockNrOrHash.Hash(); ok {
return b.GetCodeByHash(ctx, address, hash)
@@ -572,8 +662,28 @@ func (b *Backend) GetCodeByNumberOrHash(ctx context.Context, address common.Addr
}
// GetCodeByNumber returns the byte code for the contract deployed at the provided address at the canonical block with the provided block number
func (b *Backend) GetCodeByNumber(ctx context.Context, address common.Address, number uint64) ([]byte, error) {
hash := b.GetCanonicalHash(number)
func (b *Backend) GetCodeByNumber(ctx context.Context, address common.Address, blockNumber rpc.BlockNumber) ([]byte, error) {
var err error
number := blockNumber.Int64()
if blockNumber == rpc.LatestBlockNumber {
number, err = b.Retriever.RetrieveLastBlockNumber()
if err != nil {
return nil, err
}
}
if blockNumber == rpc.EarliestBlockNumber {
number, err = b.Retriever.RetrieveFirstBlockNumber()
if err != nil {
return nil, err
}
}
if blockNumber == rpc.PendingBlockNumber {
return nil, errPendingBlockNumber
}
hash, err := b.GetCanonicalHash(uint64(number))
if err != nil {
return nil, err
}
if hash == (common.Hash{}) {
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
}
@@ -599,10 +709,12 @@ func (b *Backend) GetCodeByHash(ctx context.Context, address common.Address, has
err = tx.Commit()
}
}()
if err := tx.Get(&codeHash, RetrieveCodeHashByLeafKeyAndBlockHash, leafKey.Hex(), hash.Hex()); err != nil {
err = tx.Get(&codeHash, RetrieveCodeHashByLeafKeyAndBlockHash, leafKey.Hex(), hash.Hex())
if err != nil {
return nil, err
}
mhKey, err := shared2.MultihashKeyFromKeccak256(common.BytesToHash(codeHash))
var mhKey string
mhKey, err = shared2.MultihashKeyFromKeccak256(common.BytesToHash(codeHash))
if err != nil {
return nil, err
}
@@ -612,28 +724,55 @@ func (b *Backend) GetCodeByHash(ctx context.Context, address common.Address, has
}
// GetStorageByNumberOrHash returns the storage value for the provided contract address an storage key at the block corresponding to the provided number or hash
func (b *Backend) GetStorageByNumberOrHash(ctx context.Context, address common.Address, storageLeafKey common.Hash, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
func (b *Backend) GetStorageByNumberOrHash(ctx context.Context, address common.Address, key common.Hash, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
if blockNr, ok := blockNrOrHash.Number(); ok {
return b.GetStorageByNumber(ctx, address, storageLeafKey, uint64(blockNr.Int64()))
return b.GetStorageByNumber(ctx, address, key, blockNr)
}
if hash, ok := blockNrOrHash.Hash(); ok {
return b.GetStorageByHash(ctx, address, storageLeafKey, hash)
return b.GetStorageByHash(ctx, address, key, hash)
}
return nil, errors.New("invalid arguments; neither block nor hash specified")
}
// GetStorageByNumber returns the storage value for the provided contract address an storage key at the block corresponding to the provided number
func (b *Backend) GetStorageByNumber(ctx context.Context, address common.Address, storageLeafKey common.Hash, number uint64) (hexutil.Bytes, error) {
hash := b.GetCanonicalHash(number)
if hash == (common.Hash{}) {
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
func (b *Backend) GetStorageByNumber(ctx context.Context, address common.Address, key common.Hash, blockNumber rpc.BlockNumber) (hexutil.Bytes, error) {
var err error
number := blockNumber.Int64()
if blockNumber == rpc.LatestBlockNumber {
number, err = b.Retriever.RetrieveLastBlockNumber()
if err != nil {
return nil, err
}
}
return b.GetStorageByHash(ctx, address, storageLeafKey, hash)
if blockNumber == rpc.EarliestBlockNumber {
number, err = b.Retriever.RetrieveFirstBlockNumber()
if err != nil {
return nil, err
}
}
if blockNumber == rpc.PendingBlockNumber {
return nil, errPendingBlockNumber
}
hash, err := b.GetCanonicalHash(uint64(number))
if err == sql.ErrNoRows {
return nil, errHeaderNotFound
} else if err != nil {
return nil, err
}
return b.GetStorageByHash(ctx, address, key, hash)
}
// GetStorageByHash returns the storage value for the provided contract address an storage key at the block corresponding to the provided hash
func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address, storageLeafKey, hash common.Hash) (hexutil.Bytes, error) {
_, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(address, storageLeafKey, hash)
func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address, key, hash common.Hash) (hexutil.Bytes, error) {
_, err := b.HeaderByHash(context.Background(), hash)
if err == sql.ErrNoRows {
return nil, errHeaderHashNotFound
} else if err != nil {
return nil, err
}
_, _, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash)
return storageRlp, err
}
+114 -18
View File
@@ -23,12 +23,13 @@ import (
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
)
@@ -36,7 +37,7 @@ import (
// RPCMarshalHeader converts the given header to the RPC output.
// This function is eth/internal so we have to make our own version here...
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
return map[string]interface{}{
headerMap := map[string]interface{}{
"number": (*hexutil.Big)(head.Number),
"hash": head.Hash(),
"parentHash": head.ParentHash,
@@ -55,6 +56,11 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
"transactionsRoot": head.TxHash,
"receiptsRoot": head.ReceiptHash,
}
if head.BaseFee != nil {
headerMap["baseFee"] = head.BaseFee
}
return headerMap
}
// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
@@ -134,14 +140,15 @@ func NewRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa
// NewRPCTransaction returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available).
func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction {
var signer types.Signer = types.FrontierSigner{}
func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction {
var signer types.Signer
if tx.Protected() {
signer = types.NewEIP155Signer(tx.ChainId())
signer = types.LatestSignerForChainID(tx.ChainId())
} else {
signer = types.HomesteadSigner{}
}
from, _ := types.Sender(signer, tx)
v, r, s := tx.RawSignatureValues()
result := &RPCTransaction{
From: from,
Gas: hexutil.Uint64(tx.Gas()),
@@ -151,6 +158,7 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
Nonce: hexutil.Uint64(tx.Nonce()),
To: tx.To(),
Value: (*hexutil.Big)(tx.Value()),
Type: hexutil.Uint64(tx.Type()),
V: (*hexutil.Big)(v),
R: (*hexutil.Big)(r),
S: (*hexutil.Big)(s),
@@ -160,6 +168,26 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
result.TransactionIndex = (*hexutil.Uint64)(&index)
}
switch tx.Type() {
case types.AccessListTxType:
al := tx.AccessList()
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
case types.DynamicFeeTxType:
al := tx.AccessList()
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
// if the transaction has been mined, compute the effective gas price
if baseFee != nil && blockHash != (common.Hash{}) {
// price = min(tip, gasFeeCap - baseFee) + baseFee
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
result.GasPrice = (*hexutil.Big)(price)
} else {
result.GasPrice = nil
}
}
return result
}
@@ -240,27 +268,95 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransacti
if index >= uint64(len(txs)) {
return nil
}
return NewRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index)
return NewRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee())
}
// extractLogsOfInterest returns logs from the receipt IPLD
func extractLogsOfInterest(rctIPLDs []ipfs.BlockModel, wantedTopics [][]string) ([]*types.Log, error) {
var logs []*types.Log
for _, rctIPLD := range rctIPLDs {
rctRLP := rctIPLD
var rct types.Receipt
if err := rlp.DecodeBytes(rctRLP.Data, &rct); err != nil {
func extractLogsOfInterest(config *params.ChainConfig, blockHash common.Hash, blockNumber uint64,
txs types.Transactions, rctIPLDs []ipfs.BlockModel, filter ReceiptFilter) ([]*types.Log, error) {
receipts := make(types.Receipts, len(rctIPLDs))
for i, rctBytes := range rctIPLDs {
rct := new(types.Receipt)
if err := rct.UnmarshalBinary(rctBytes.Data); err != nil {
return nil, err
}
for _, log := range rct.Logs {
if wanted := wantedLog(wantedTopics, log.Topics); wanted == true {
logs = append(logs, log)
}
receipts[i] = rct
}
err := receipts.DeriveFields(config, blockHash, blockNumber, txs)
if err != nil {
return nil, err
}
var unfilteredLogs []*types.Log
for _, receipt := range receipts {
unfilteredLogs = append(unfilteredLogs, receipt.Logs...)
}
adders := make([]common.Address, len(filter.LogAddresses))
for i, addr := range filter.LogAddresses {
adders[i] = common.HexToAddress(addr)
}
topics := make([][]common.Hash, len(filter.Topics))
for i, v := range filter.Topics {
topics[i] = make([]common.Hash, len(v))
for j, topic := range v {
topics[i][j] = common.HexToHash(topic)
}
}
logs := filterLogs(unfilteredLogs, nil, nil, adders, topics)
return logs, nil
}
func includes(addresses []common.Address, a common.Address) bool {
for _, addr := range addresses {
if addr == a {
return true
}
}
return false
}
// filterLogs creates a slice of logs matching the given criteria.
func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
var ret []*types.Log
Logs:
for _, log := range logs {
if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
continue
}
if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
continue
}
if len(addresses) > 0 && !includes(addresses, log.Address) {
continue
}
// If the to filtered topics is greater than the amount of topics in logs, skip.
if len(topics) > len(log.Topics) {
continue
}
for i, sub := range topics {
match := len(sub) == 0 // empty rule set == wildcard
for _, topic := range sub {
if log.Topics[i] == topic {
match = true
break
}
}
if !match {
continue Logs
}
}
ret = append(ret, log)
}
return ret
}
// returns true if the log matches on the filter
func wantedLog(wantedTopics [][]string, actualTopics []common.Hash) bool {
// actualTopics will always have length <= 4
+29 -10
View File
@@ -86,7 +86,8 @@ func (ecr *CIDRetriever) Retrieve(filter SubscriptionSettings, blockNumber int64
}()
// Retrieve cached header CIDs at this block height
headers, err := ecr.RetrieveHeaderCIDs(tx, blockNumber)
var headers []eth.HeaderModel
headers, err = ecr.RetrieveHeaderCIDs(tx, blockNumber)
if err != nil {
log.Error("header cid retrieval error")
return nil, true, err
@@ -101,7 +102,8 @@ func (ecr *CIDRetriever) Retrieve(filter SubscriptionSettings, blockNumber int64
empty = false
if filter.HeaderFilter.Uncles {
// Retrieve uncle cids for this header id
uncleCIDs, err := ecr.RetrieveUncleCIDsByHeaderID(tx, header.ID)
var uncleCIDs []eth.UncleModel
uncleCIDs, err = ecr.RetrieveUncleCIDsByHeaderID(tx, header.ID)
if err != nil {
log.Error("uncle cid retrieval error")
return nil, true, err
@@ -310,6 +312,14 @@ func (ecr *CIDRetriever) RetrieveRctCIDs(tx *sqlx.Tx, rctFilter ReceiptFilter, b
args = append(args, blockHash.String())
id++
}
// TODO: Add the below filters when we have log index in DB.
if true {
pgStr += ` ORDER BY transaction_cids.index`
receiptCids := make([]eth.ReceiptModel, 0)
return receiptCids, tx.Select(&receiptCids, pgStr, args...)
}
if len(rctFilter.LogAddresses) > 0 {
// Filter on log contract addresses if there are any
pgStr += fmt.Sprintf(` AND ((receipt_cids.log_contracts && $%d::VARCHAR(66)[]`, id)
@@ -371,6 +381,7 @@ func (ecr *CIDRetriever) RetrieveRctCIDs(tx *sqlx.Tx, rctFilter ReceiptFilter, b
args = append(args, pq.Array(trxIds))
}
}
pgStr += ` ORDER BY transaction_cids.index`
receiptCids := make([]eth.ReceiptModel, 0)
return receiptCids, tx.Select(&receiptCids, pgStr, args...)
@@ -463,17 +474,20 @@ func (ecr *CIDRetriever) RetrieveBlockByHash(blockHash common.Hash) (eth.HeaderM
}
}()
headerCID, err := ecr.RetrieveHeaderCIDByHash(tx, blockHash)
var headerCID eth.HeaderModel
headerCID, err = ecr.RetrieveHeaderCIDByHash(tx, blockHash)
if err != nil {
log.Error("header cid retrieval error")
return eth.HeaderModel{}, nil, nil, nil, err
}
uncleCIDs, err := ecr.RetrieveUncleCIDsByHeaderID(tx, headerCID.ID)
var uncleCIDs []eth.UncleModel
uncleCIDs, err = ecr.RetrieveUncleCIDsByHeaderID(tx, headerCID.ID)
if err != nil {
log.Error("uncle cid retrieval error")
return eth.HeaderModel{}, nil, nil, nil, err
}
txCIDs, err := ecr.RetrieveTxCIDsByHeaderID(tx, headerCID.ID)
var txCIDs []eth.TxModel
txCIDs, err = ecr.RetrieveTxCIDsByHeaderID(tx, headerCID.ID)
if err != nil {
log.Error("tx cid retrieval error")
return eth.HeaderModel{}, nil, nil, nil, err
@@ -482,7 +496,8 @@ func (ecr *CIDRetriever) RetrieveBlockByHash(blockHash common.Hash) (eth.HeaderM
for i, txCID := range txCIDs {
txIDs[i] = txCID.ID
}
rctCIDs, err := ecr.RetrieveReceiptCIDsByTxIDs(tx, txIDs)
var rctCIDs []eth.ReceiptModel
rctCIDs, err = ecr.RetrieveReceiptCIDsByTxIDs(tx, txIDs)
if err != nil {
log.Error("rct cid retrieval error")
}
@@ -509,7 +524,8 @@ func (ecr *CIDRetriever) RetrieveBlockByNumber(blockNumber int64) (eth.HeaderMod
}
}()
headerCID, err := ecr.RetrieveHeaderCIDs(tx, blockNumber)
var headerCID []eth.HeaderModel
headerCID, err = ecr.RetrieveHeaderCIDs(tx, blockNumber)
if err != nil {
log.Error("header cid retrieval error")
return eth.HeaderModel{}, nil, nil, nil, err
@@ -517,12 +533,14 @@ func (ecr *CIDRetriever) RetrieveBlockByNumber(blockNumber int64) (eth.HeaderMod
if len(headerCID) < 1 {
return eth.HeaderModel{}, nil, nil, nil, fmt.Errorf("header cid retrieval error, no header CIDs found at block %d", blockNumber)
}
uncleCIDs, err := ecr.RetrieveUncleCIDsByHeaderID(tx, headerCID[0].ID)
var uncleCIDs []eth.UncleModel
uncleCIDs, err = ecr.RetrieveUncleCIDsByHeaderID(tx, headerCID[0].ID)
if err != nil {
log.Error("uncle cid retrieval error")
return eth.HeaderModel{}, nil, nil, nil, err
}
txCIDs, err := ecr.RetrieveTxCIDsByHeaderID(tx, headerCID[0].ID)
var txCIDs []eth.TxModel
txCIDs, err = ecr.RetrieveTxCIDsByHeaderID(tx, headerCID[0].ID)
if err != nil {
log.Error("tx cid retrieval error")
return eth.HeaderModel{}, nil, nil, nil, err
@@ -531,7 +549,8 @@ func (ecr *CIDRetriever) RetrieveBlockByNumber(blockNumber int64) (eth.HeaderMod
for i, txCID := range txCIDs {
txIDs[i] = txCID.ID
}
rctCIDs, err := ecr.RetrieveReceiptCIDsByTxIDs(tx, txIDs)
var rctCIDs []eth.ReceiptModel
rctCIDs, err = ecr.RetrieveReceiptCIDsByTxIDs(tx, txIDs)
if err != nil {
log.Error("rct cid retrieval error")
}
+2
View File
@@ -237,6 +237,7 @@ var _ = Describe("Retriever", func() {
Expect(empty).ToNot(BeTrue())
Expect(len(cids)).To(Equal(1))
Expect(cids[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
expectedHeaderCID := test_helpers.MockCIDWrapper.Header
expectedHeaderCID.ID = cids[0].Header.ID
expectedHeaderCID.NodeID = cids[0].Header.NodeID
@@ -250,6 +251,7 @@ var _ = Describe("Retriever", func() {
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, test_helpers.MockCIDWrapper.Receipts[1].CID)).To(BeTrue())
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, test_helpers.MockCIDWrapper.Receipts[2].CID)).To(BeTrue())
Expect(len(cids[0].StateNodes)).To(Equal(2))
for _, stateNode := range cids[0].StateNodes {
if stateNode.CID == test_helpers.State1CID.String() {
Expect(stateNode.StateKey).To(Equal(common.BytesToHash(test_helpers.ContractLeafKey).Hex()))
+105 -58
View File
@@ -38,7 +38,6 @@ import (
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
"github.com/vulcanize/ipld-eth-indexer/pkg/shared"
"github.com/vulcanize/ipld-eth-server/pkg/eth"
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
)
@@ -60,6 +59,7 @@ func init() {
}
var _ = Describe("eth state reading tests", func() {
const chainLength = 5
var (
blocks []*types.Block
receipts []types.Receipts
@@ -80,13 +80,13 @@ var _ = Describe("eth state reading tests", func() {
backend, err = eth.NewEthBackend(db, &eth.Config{
ChainConfig: chainConfig,
VmConfig: vm.Config{},
RPCGasCap: big.NewInt(10000000000),
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
})
Expect(err).ToNot(HaveOccurred())
api = eth.NewPublicEthAPI(backend, nil, false)
// make the test blockchain (and state)
blocks, receipts, chain = test_helpers.MakeChain(5, test_helpers.Genesis, test_helpers.TestChainGen)
blocks, receipts, chain = test_helpers.MakeChain(chainLength, test_helpers.Genesis, test_helpers.TestChainGen)
params := statediff.Params{
IntermediateStateNodes: true,
IntermediateStorageNodes: true,
@@ -234,12 +234,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(1))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(1))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(1))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(1))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(1))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(1))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock1))
@@ -247,12 +250,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(2))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(2))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(2))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(2))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
@@ -260,12 +266,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(3))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(3))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(3))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(3))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
@@ -273,12 +282,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(4))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(4))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(4))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(4))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
@@ -286,12 +298,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(5))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(5))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(5))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(5))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
@@ -304,12 +319,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock1))
@@ -317,12 +335,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
@@ -330,12 +351,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
@@ -343,12 +367,15 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
@@ -356,37 +383,45 @@ var _ = Describe("eth state reading tests", func() {
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedContractBalance))
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal(expectedBankBalanceBlock2))
})
It("Throws an error for an account it cannot find the balance for an account at the provided block number", func() {
_, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns `0` for an account it cannot find the balance for an account at the provided block number", func() {
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
})
It("Throws an error for an account it cannot find the balance for an account at the provided block hash", func() {
_, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns `0` for an error for an account it cannot find the balance for an account at the provided block hash", func() {
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
})
})
@@ -409,52 +444,64 @@ var _ = Describe("eth state reading tests", func() {
Expect(err).ToNot(HaveOccurred())
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
})
It("Throws an error for an account it cannot find the code for", func() {
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
Expect(err).To(HaveOccurred())
It("Returns `nil` for an account it cannot find the code for", func() {
code, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
Expect(err).ToNot(HaveOccurred())
Expect(code).To(BeEmpty())
})
It("Throws an error for a contract that doesn't exist at this hieght", func() {
_, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).To(HaveOccurred())
It("Returns `nil` for a contract that doesn't exist at this height", func() {
code, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
Expect(err).ToNot(HaveOccurred())
Expect(code).To(BeEmpty())
})
})
Describe("eth_getStorageAt", func() {
It("Throws an error if it tries to access a contract which does not exist", func() {
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns empty slice if it tries to access a contract which does not exist", func() {
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
Expect(err).NotTo(HaveOccurred())
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
_, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
storage, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
Expect(err).NotTo(HaveOccurred())
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
})
It("Throws an error if it tries to access a contract slot which does not exist", func() {
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
It("Returns empty slice if it tries to access a contract slot which does not exist", func() {
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
Expect(err).NotTo(HaveOccurred())
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
})
It("Retrieves the storage value at the provided contract address and storage leaf key at the block with the provided hash or number", func() {
// After deployment
val, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
val, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(2))
Expect(err).ToNot(HaveOccurred())
expectedRes := hexutil.Bytes(common.Hex2Bytes("01"))
expectedRes := hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
Expect(val).To(Equal(expectedRes))
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(3))
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(3))
Expect(err).ToNot(HaveOccurred())
expectedRes = hexutil.Bytes(common.Hex2Bytes("03"))
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
Expect(val).To(Equal(expectedRes))
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(4))
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(4))
Expect(err).ToNot(HaveOccurred())
expectedRes = hexutil.Bytes(common.Hex2Bytes("09"))
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000009"))
Expect(val).To(Equal(expectedRes))
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(5))
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(5))
Expect(err).ToNot(HaveOccurred())
Expect(val).To(Equal(hexutil.Bytes{}))
})
It("Throws an error for a non-existing block hash", func() {
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithHash(randomHash, true))
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError("header for hash not found"))
})
It("Throws an error for a non-existing block number", func() {
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(chainLength+1))
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError("header not found"))
})
})
Describe("eth_getHeaderByNumber", func() {
+13 -13
View File
@@ -50,13 +50,13 @@ var _ = Describe("Filterer", func() {
var expectedEmptyUncles []ipfs.BlockModel
Expect(iplds.Uncles).To(Equal(expectedEmptyUncles))
Expect(len(iplds.Transactions)).To(Equal(3))
Expect(shared.IPLDsContainBytes(iplds.Transactions, test_helpers.MockTransactions.GetRlp(0))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Transactions, test_helpers.MockTransactions.GetRlp(1))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Transactions, test_helpers.MockTransactions.GetRlp(2))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Transactions, test_helpers.Tx1)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Transactions, test_helpers.Tx2)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Transactions, test_helpers.Tx3)).To(BeTrue())
Expect(len(iplds.Receipts)).To(Equal(3))
Expect(shared.IPLDsContainBytes(iplds.Receipts, test_helpers.MockReceipts.GetRlp(0))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Receipts, test_helpers.MockReceipts.GetRlp(1))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Receipts, test_helpers.MockReceipts.GetRlp(2))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Receipts, test_helpers.Rct1)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Receipts, test_helpers.Rct2)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds.Receipts, test_helpers.Rct3)).To(BeTrue())
Expect(len(iplds.StateNodes)).To(Equal(2))
for _, stateNode := range iplds.StateNodes {
Expect(stateNode.Type).To(Equal(sdtypes.Leaf))
@@ -144,15 +144,15 @@ var _ = Describe("Filterer", func() {
Expect(iplds5.Header).To(Equal(ipfs.BlockModel{}))
Expect(len(iplds5.Uncles)).To(Equal(0))
Expect(len(iplds5.Transactions)).To(Equal(3))
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.MockTransactions.GetRlp(0))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.MockTransactions.GetRlp(1))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.MockTransactions.GetRlp(2))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.Tx1)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.Tx2)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.Tx3)).To(BeTrue())
Expect(len(iplds5.StorageNodes)).To(Equal(0))
Expect(len(iplds5.StateNodes)).To(Equal(0))
Expect(len(iplds5.Receipts)).To(Equal(3))
Expect(shared.IPLDsContainBytes(iplds5.Receipts, test_helpers.MockReceipts.GetRlp(0))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Receipts, test_helpers.MockReceipts.GetRlp(1))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Receipts, test_helpers.MockReceipts.GetRlp(2))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Receipts, test_helpers.Rct1)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Receipts, test_helpers.Rct2)).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Receipts, test_helpers.Rct3)).To(BeTrue())
iplds6, err := filterer.Filter(rctsForSelectCollectedTrxs, test_helpers.MockConvertedPayload)
Expect(err).ToNot(HaveOccurred())
@@ -161,7 +161,7 @@ var _ = Describe("Filterer", func() {
Expect(iplds6.Header).To(Equal(ipfs.BlockModel{}))
Expect(len(iplds6.Uncles)).To(Equal(0))
Expect(len(iplds6.Transactions)).To(Equal(1))
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.MockTransactions.GetRlp(1))).To(BeTrue())
Expect(shared.IPLDsContainBytes(iplds5.Transactions, test_helpers.Tx2)).To(BeTrue())
Expect(len(iplds6.StorageNodes)).To(Equal(0))
Expect(len(iplds6.StateNodes)).To(Equal(0))
Expect(len(iplds6.Receipts)).To(Equal(1))
+24 -17
View File
@@ -29,15 +29,15 @@ import (
const (
RetrieveHeadersByHashesPgStr = `SELECT cid, data
FROM eth.header_cids
FROM eth.header_cids
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
WHERE block_hash = ANY($1::VARCHAR(66)[])`
RetrieveHeadersByBlockNumberPgStr = `SELECT cid, data
FROM eth.header_cids
FROM eth.header_cids
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
WHERE block_number = $1`
RetrieveHeaderByHashPgStr = `SELECT cid, data
FROM eth.header_cids
FROM eth.header_cids
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
WHERE block_hash = $1`
RetrieveUnclesByHashesPgStr = `SELECT cid, data
@@ -83,7 +83,7 @@ const (
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
WHERE tx_hash = ANY($1::VARCHAR(66)[])`
RetrieveReceiptsByBlockHashPgStr = `SELECT receipt_cids.cid, data
RetrieveReceiptsByBlockHashPgStr = `SELECT receipt_cids.cid, data, eth.transaction_cids.tx_hash
FROM eth.receipt_cids
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
@@ -161,9 +161,11 @@ const (
)
type ipldResult struct {
CID string `db:"cid"`
Data []byte `db:"data"`
CID string `db:"cid"`
Data []byte `db:"data"`
TxHash string `db:"tx_hash"`
}
type IPLDRetriever struct {
db *postgres.DB
}
@@ -345,18 +347,22 @@ func (r *IPLDRetriever) RetrieveReceiptsByTxHashes(hashes []common.Hash) ([]stri
}
// RetrieveReceiptsByBlockHash returns the cids and rlp bytes for the receipts corresponding to the provided block hash
func (r *IPLDRetriever) RetrieveReceiptsByBlockHash(hash common.Hash) ([]string, [][]byte, error) {
func (r *IPLDRetriever) RetrieveReceiptsByBlockHash(hash common.Hash) ([]string, [][]byte, []common.Hash, error) {
rctResults := make([]ipldResult, 0)
if err := r.db.Select(&rctResults, RetrieveReceiptsByBlockHashPgStr, hash.Hex()); err != nil {
return nil, nil, err
return nil, nil, nil, err
}
cids := make([]string, len(rctResults))
rcts := make([][]byte, len(rctResults))
txs := make([]common.Hash, len(rctResults))
for i, res := range rctResults {
cids[i] = res.CID
rcts[i] = res.Data
txs[i] = common.HexToHash(res.TxHash)
}
return cids, rcts, nil
return cids, rcts, txs, nil
}
// RetrieveReceiptsByBlockNumber returns the cids and rlp bytes for the receipts corresponding to the provided block hash
@@ -428,25 +434,26 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Ad
return accountResult.CID, i[1].([]byte), nil
}
// RetrieveStorageAtByAddressAndStorageKeyAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage key, and block hash
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(address common.Address, storageLeafKey, hash common.Hash) (string, []byte, error) {
// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, []byte, error) {
storageResult := new(nodeInfo)
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageLeafKey.Hex(), hash.Hex()); err != nil {
return "", nil, err
storageHash := crypto.Keccak256Hash(key.Bytes())
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
return "", nil, nil, err
}
if storageResult.Removed {
return "", []byte{}, nil
return "", []byte{}, []byte{}, nil
}
var i []interface{}
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {
err = fmt.Errorf("error decoding storage leaf node rlp: %s", err.Error())
return "", nil, err
return "", nil, nil, err
}
if len(i) != 2 {
return "", nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements")
return "", nil, nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements")
}
return storageResult.CID, i[1].([]byte), nil
return storageResult.CID, storageResult.Data, i[1].([]byte), nil
}
// RetrieveStorageAtByAddressAndStorageKeyAndBlockNumber returns the cid and rlp bytes for the storage value corresponding to the provided address, storage key, and block number
+1
View File
@@ -62,6 +62,7 @@ data function sig: 73d4a13a
// the returned hash chain is ordered head->parent.
func MakeChain(n int, parent *types.Block, chainGen func(int, *core.BlockGen)) ([]*types.Block, []types.Receipts, *core.BlockChain) {
config := params.TestChainConfig
config.LondonBlock = big.NewInt(100)
blocks, receipts := core.GenerateChain(config, parent, ethash.NewFaker(), Testdb, n, chainGen)
chain, _ := core.NewBlockChain(Testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
return append([]*types.Block{parent}, blocks...), receipts, chain
+185 -27
View File
@@ -17,6 +17,7 @@
package test_helpers
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -34,7 +35,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/statediff/testhelpers"
"github.com/ipfs/go-block-format"
blocks "github.com/ipfs/go-block-format"
"github.com/multiformats/go-multihash"
log "github.com/sirupsen/logrus"
@@ -58,7 +59,7 @@ var (
Difficulty: big.NewInt(5000000),
Extra: []byte{},
}
MockTransactions, MockReceipts, SenderAddr = createTransactionsAndReceipts()
MockTransactions, MockReceipts, SenderAddr = createLegacyTransactionsAndReceipts()
MockUncles = []*types.Header{
{
Time: 1,
@@ -80,7 +81,7 @@ var (
},
}
ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts)
MockBlock = types.NewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
MockHeaderRlp, _ = rlp.EncodeToBytes(MockBlock.Header())
MockChildHeader = types.Header{
Time: 0,
@@ -104,28 +105,43 @@ var (
mockTopic21 = common.HexToHash("0x05")
mockTopic22 = common.HexToHash("0x07")
MockLog1 = &types.Log{
Address: Address,
Topics: []common.Hash{mockTopic11, mockTopic12},
Data: []byte{},
Address: Address,
Topics: []common.Hash{mockTopic11, mockTopic12},
Data: []byte{},
BlockNumber: BlockNumber.Uint64(),
TxIndex: 0,
Index: 0,
}
MockLog2 = &types.Log{
Address: AnotherAddress,
Topics: []common.Hash{mockTopic21, mockTopic22},
Data: []byte{},
Address: AnotherAddress,
Topics: []common.Hash{mockTopic21, mockTopic22},
Data: []byte{},
BlockNumber: BlockNumber.Uint64(),
TxIndex: 1,
Index: 1,
}
Tx1 = GetTxnRlp(0, MockTransactions)
Tx2 = GetTxnRlp(1, MockTransactions)
Tx3 = GetTxnRlp(2, MockTransactions)
Rct1 = GetRctRlp(0, MockReceipts)
Rct2 = GetRctRlp(1, MockReceipts)
Rct3 = GetRctRlp(2, MockReceipts)
HeaderCID, _ = ipld.RawdataToCid(ipld.MEthHeader, MockHeaderRlp, multihash.KECCAK_256)
HeaderMhKey = shared.MultihashKeyFromCID(HeaderCID)
Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, MockTransactions.GetRlp(0), multihash.KECCAK_256)
Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx1, multihash.KECCAK_256)
Trx1MhKey = shared.MultihashKeyFromCID(Trx1CID)
Trx2CID, _ = ipld.RawdataToCid(ipld.MEthTx, MockTransactions.GetRlp(1), multihash.KECCAK_256)
Trx2CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx2, multihash.KECCAK_256)
Trx2MhKey = shared.MultihashKeyFromCID(Trx2CID)
Trx3CID, _ = ipld.RawdataToCid(ipld.MEthTx, MockTransactions.GetRlp(2), multihash.KECCAK_256)
Trx3CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx3, multihash.KECCAK_256)
Trx3MhKey = shared.MultihashKeyFromCID(Trx3CID)
Rct1CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, MockReceipts.GetRlp(0), multihash.KECCAK_256)
Rct1CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, Rct1, multihash.KECCAK_256)
Rct1MhKey = shared.MultihashKeyFromCID(Rct1CID)
Rct2CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, MockReceipts.GetRlp(1), multihash.KECCAK_256)
Rct2CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, Rct2, multihash.KECCAK_256)
Rct2MhKey = shared.MultihashKeyFromCID(Rct2CID)
Rct3CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, MockReceipts.GetRlp(2), multihash.KECCAK_256)
Rct3CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, Rct3, multihash.KECCAK_256)
Rct3MhKey = shared.MultihashKeyFromCID(Rct3CID)
State1CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, ContractLeafNode, multihash.KECCAK_256)
State1MhKey = shared.MultihashKeyFromCID(State1CID)
@@ -375,6 +391,8 @@ var (
StateNodes: MockStateNodes,
}
Reward = eth.CalcEthBlockReward(MockBlock.Header(), MockBlock.Uncles(), MockBlock.Transactions(), MockReceipts)
MockCIDWrapper = &eth2.CIDWrapper{
BlockNumber: new(big.Int).Set(BlockNumber),
Header: eth.HeaderModel{
@@ -384,7 +402,7 @@ var (
CID: HeaderCID.String(),
MhKey: HeaderMhKey,
TotalDifficulty: MockBlock.Difficulty().String(),
Reward: "5312500000000000000",
Reward: Reward.String(),
StateRoot: MockBlock.Root().String(),
RctRoot: MockBlock.ReceiptHash().String(),
TxRoot: MockBlock.TxHash().String(),
@@ -410,12 +428,12 @@ var (
}
HeaderIPLD, _ = blocks.NewBlockWithCid(MockHeaderRlp, HeaderCID)
Trx1IPLD, _ = blocks.NewBlockWithCid(MockTransactions.GetRlp(0), Trx1CID)
Trx2IPLD, _ = blocks.NewBlockWithCid(MockTransactions.GetRlp(1), Trx2CID)
Trx3IPLD, _ = blocks.NewBlockWithCid(MockTransactions.GetRlp(2), Trx3CID)
Rct1IPLD, _ = blocks.NewBlockWithCid(MockReceipts.GetRlp(0), Rct1CID)
Rct2IPLD, _ = blocks.NewBlockWithCid(MockReceipts.GetRlp(1), Rct2CID)
Rct3IPLD, _ = blocks.NewBlockWithCid(MockReceipts.GetRlp(2), Rct3CID)
Trx1IPLD, _ = blocks.NewBlockWithCid(Tx1, Trx1CID)
Trx2IPLD, _ = blocks.NewBlockWithCid(Tx2, Trx2CID)
Trx3IPLD, _ = blocks.NewBlockWithCid(Tx3, Trx3CID)
Rct1IPLD, _ = blocks.NewBlockWithCid(Rct1, Rct1CID)
Rct2IPLD, _ = blocks.NewBlockWithCid(Rct2, Rct2CID)
Rct3IPLD, _ = blocks.NewBlockWithCid(Rct3, Rct3CID)
State1IPLD, _ = blocks.NewBlockWithCid(ContractLeafNode, State1CID)
State2IPLD, _ = blocks.NewBlockWithCid(AccountLeafNode, State2CID)
StorageIPLD, _ = blocks.NewBlockWithCid(StorageLeafNode, StorageCID)
@@ -487,10 +505,119 @@ var (
},
},
}
LondonBlockNum = new(big.Int).Add(BlockNumber, big.NewInt(2))
MockLondonHeader = types.Header{
Time: 0,
Number: LondonBlockNum,
Root: common.HexToHash("0x00"),
Difficulty: big.NewInt(5000000),
Extra: []byte{},
BaseFee: big.NewInt(params.InitialBaseFee),
}
MockLondonTransactions, MockLondonReceipts, SenderAdd = createDynamicTransactionsAndReceipts(LondonBlockNum)
MockLondonBlock = createNewBlock(&MockLondonHeader, MockLondonTransactions, nil, MockLondonReceipts, new(trie.Trie))
MockLondonTrxMeta = []eth.TxModel{
{
CID: "", // This is empty until we go to publish to ipfs
MhKey: "",
Src: SenderAdd.Hex(),
Dst: Address.String(),
Index: 0,
TxHash: MockLondonTransactions[0].Hash().String(),
Data: []byte{},
},
}
MockLondonRctMeta = []eth.ReceiptModel{
{
CID: "",
MhKey: "",
Topic0s: []string{
mockTopic11.String(),
},
Topic1s: []string{
mockTopic12.String(),
},
Contract: "",
ContractHash: "",
LogContracts: []string{
Address.String(),
},
},
}
MockConvertedLondonPayload = eth.ConvertedPayload{
TotalDifficulty: MockLondonBlock.Difficulty(),
Block: MockLondonBlock,
Receipts: MockLondonReceipts,
TxMetaData: MockLondonTrxMeta,
ReceiptMetaData: MockLondonRctMeta,
StorageNodes: MockStorageNodes,
StateNodes: MockStateNodes,
}
)
// 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 createNewBlock(header *types.Header, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, hasher types.TrieHasher) *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
}
// createDynamicTransactionsAndReceipts is a helper function to generate signed mock transactions and mock receipts with mock logs
func createDynamicTransactionsAndReceipts(blockNumber *big.Int) (types.Transactions, types.Receipts, common.Address) {
// make transactions
config := params.TestChainConfig
config.LondonBlock = blockNumber
trx1 := types.NewTx(&types.DynamicFeeTx{
ChainID: config.ChainID,
Nonce: 1,
GasTipCap: big.NewInt(50),
GasFeeCap: big.NewInt(100),
Gas: 50,
To: &Address,
Value: big.NewInt(1000),
Data: []byte{},
})
transactionSigner := types.MakeSigner(config, blockNumber)
mockCurve := elliptic.P256()
mockPrvKey, err := ecdsa.GenerateKey(mockCurve, rand.Reader)
if err != nil {
log.Fatal(err.Error())
}
signedTrx1, err := types.SignTx(trx1, transactionSigner, mockPrvKey)
if err != nil {
log.Fatal(err.Error())
}
senderAddr, err := types.Sender(transactionSigner, signedTrx1) // same for both trx
if err != nil {
log.Fatal(err.Error())
}
// make receipts
// TODO: Change the receipt type to DynamicFeeTxType once this PR is merged.
// https://github.com/ethereum/go-ethereum/pull/22806
mockReceipt1 := &types.Receipt{
Type: types.AccessListTxType,
PostState: common.HexToHash("0x1").Bytes(),
Status: types.ReceiptStatusSuccessful,
CumulativeGasUsed: 50,
Logs: []*types.Log{},
TxHash: signedTrx1.Hash(),
}
return types.Transactions{signedTrx1}, types.Receipts{mockReceipt1}, senderAddr
}
// createLegacyTransactionsAndReceipts is a helper function to generate signed mock transactions and mock receipts with mock logs
func createLegacyTransactionsAndReceipts() (types.Transactions, types.Receipts, common.Address) {
// make transactions
trx1 := types.NewTransaction(0, Address, big.NewInt(1000), 50, big.NewInt(100), []byte{})
trx2 := types.NewTransaction(1, AnotherAddress, big.NewInt(2000), 100, big.NewInt(200), []byte{})
@@ -519,13 +646,44 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
}
// make receipts
mockReceipt1 := types.NewReceipt(common.HexToHash("0x0").Bytes(), false, 50)
hash1 := signedTrx1.Hash()
MockLog1.TxHash = hash1
mockReceipt1.Logs = []*types.Log{MockLog1}
mockReceipt1.TxHash = signedTrx1.Hash()
mockReceipt1.TxHash = hash1
mockReceipt1.GasUsed = mockReceipt1.CumulativeGasUsed
mockReceipt2 := types.NewReceipt(common.HexToHash("0x1").Bytes(), false, 100)
hash2 := signedTrx2.Hash()
MockLog2.TxHash = hash2
mockReceipt2.Logs = []*types.Log{MockLog2}
mockReceipt2.TxHash = signedTrx2.Hash()
mockReceipt3 := types.NewReceipt(common.HexToHash("0x2").Bytes(), false, 75)
mockReceipt2.TxHash = hash2
mockReceipt2.GasUsed = mockReceipt2.CumulativeGasUsed - mockReceipt1.CumulativeGasUsed
mockReceipt3 := types.NewReceipt(common.HexToHash("0x2").Bytes(), false, 175)
mockReceipt3.Logs = []*types.Log{}
mockReceipt3.TxHash = signedTrx3.Hash()
mockReceipt3.GasUsed = mockReceipt3.CumulativeGasUsed - mockReceipt2.CumulativeGasUsed
return types.Transactions{signedTrx1, signedTrx2, signedTrx3}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3}, SenderAddr
}
func GetTxnRlp(num int, txs types.Transactions) []byte {
buf := new(bytes.Buffer)
txs.EncodeIndex(num, buf)
tx := make([]byte, buf.Len())
copy(tx, buf.Bytes())
buf.Reset()
return tx
}
func GetRctRlp(num int, rcts types.Receipts) []byte {
buf := new(bytes.Buffer)
rcts.EncodeIndex(num, buf)
rct := make([]byte, buf.Len())
copy(rct, buf.Bytes())
buf.Reset()
return rct
}
+29 -20
View File
@@ -29,20 +29,25 @@ import (
// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
type RPCTransaction struct {
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
Value *hexutil.Big `json:"value"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
Value *hexutil.Big `json:"value"`
Type hexutil.Uint64 `json:"type"`
Accesses *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
}
// RPCReceipt represents a receipt that will serialize to the RPC representation of a receipt
@@ -82,12 +87,16 @@ type StorageResult struct {
// CallArgs represents the arguments for a call.
type CallArgs struct {
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Value *hexutil.Big `json:"value"`
Data *hexutil.Bytes `json:"data"`
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
Value *hexutil.Big `json:"value"`
Data *hexutil.Bytes `json:"data"`
AccessList *types.AccessList `json:"accessList,omitempty"`
Input *hexutil.Bytes `json:"input"`
}
// account indicates the overriding fields of account during the execution of
+112
View File
@@ -0,0 +1,112 @@
package graphql
import (
"context"
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gqlclient "github.com/machinebox/graphql"
)
type StorageResponse struct {
Cid string `json:"cid"`
Value common.Hash `json:"value"`
IpldBlock hexutil.Bytes `json:"ipldBlock"`
}
type GetStorageAt struct {
Response StorageResponse `json:"getStorageAt"`
}
type LogResponse struct {
Topics []common.Hash `json:"topics"`
Data hexutil.Bytes `json:"data"`
Transaction TransactionResp `json:"transaction"`
}
type TransactionResp struct {
Hash common.Hash `json:"hash"`
}
type GetLogs struct {
Responses []LogResponse `json:"getLogs"`
}
type Client struct {
client *gqlclient.Client
}
func NewClient(endpoint string) *Client {
client := gqlclient.NewClient(endpoint)
return &Client{client: client}
}
func (c *Client) GetLogs(ctx context.Context, hash common.Hash, address common.Address) ([]LogResponse, error) {
getLogsQuery := fmt.Sprintf(`
query{
getLogs(blockHash: "%s", contract: "%s") {
data
topics
transaction {
hash
}
}
}
`, hash.String(), address.String())
req := gqlclient.NewRequest(getLogsQuery)
req.Header.Set("Cache-Control", "no-cache")
var respData map[string]interface{}
err := c.client.Run(ctx, req, &respData)
if err != nil {
return nil, err
}
jsonStr, err := json.Marshal(respData)
if err != nil {
return nil, err
}
var logs GetLogs
err = json.Unmarshal(jsonStr, &logs)
if err != nil {
return nil, err
}
return logs.Responses, nil
}
func (c *Client) GetStorageAt(ctx context.Context, hash common.Hash, address common.Address, slot string) (*StorageResponse, error) {
getLogsQuery := fmt.Sprintf(`
query{
getStorageAt(blockHash: "%s", contract: "%s",slot: "%s") {
cid
value
ipldBlock
}
}
`, hash.String(), address.String(), common.HexToHash(slot))
req := gqlclient.NewRequest(getLogsQuery)
req.Header.Set("Cache-Control", "no-cache")
var respData map[string]interface{}
err := c.client.Run(ctx, req, &respData)
if err != nil {
return nil, err
}
jsonStr, err := json.Marshal(respData)
if err != nil {
return nil, err
}
var storageAt GetStorageAt
err = json.Unmarshal(jsonStr, &storageAt)
if err != nil {
return nil, err
}
return &storageAt.Response, nil
}
+105 -1
View File
@@ -19,6 +19,7 @@ package graphql
import (
"context"
"database/sql"
"errors"
"time"
@@ -28,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/vulcanize/ipld-eth-server/pkg/eth"
@@ -91,6 +93,8 @@ type Log struct {
backend *eth.Backend
transaction *Transaction
log *types.Log
cid string
ipldBlock []byte
}
func (l *Log) Transaction(ctx context.Context) *Transaction {
@@ -117,6 +121,14 @@ func (l *Log) Data(ctx context.Context) hexutil.Bytes {
return hexutil.Bytes(l.log.Data)
}
func (l *Log) Cid(ctx context.Context) string {
return l.cid
}
func (l *Log) IpldBlock(ctx context.Context) hexutil.Bytes {
return hexutil.Bytes(l.ipldBlock)
}
// Transaction represents an Ethereum transaction.
// backend and hash are mandatory; all others will be fetched when required.
type Transaction struct {
@@ -867,7 +879,11 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
if args.To != nil {
to = rpc.BlockNumber(*args.To)
} else {
to = rpc.BlockNumber(r.backend.CurrentBlock().Number().Int64())
block, err := r.backend.CurrentBlock()
if err != nil {
return []*Block{}, nil
}
to = rpc.BlockNumber(block.Number().Int64())
}
if to < from {
return []*Block{}, nil
@@ -940,3 +956,91 @@ func (r *Resolver) Logs(ctx context.Context, args struct{ Filter FilterCriteria
filter := filters.NewRangeFilter(filters.Backend(r.backend), begin, end, addresses, topics)
return runFilter(ctx, r.backend, filter)
}
// StorageResult represents a storage slot value. All arguments are mandatory.
type StorageResult struct {
value []byte
cid string
ipldBlock []byte
}
func (s *StorageResult) Value(ctx context.Context) common.Hash {
return common.BytesToHash(s.value)
}
func (s *StorageResult) Cid(ctx context.Context) string {
return s.cid
}
func (s *StorageResult) IpldBlock(ctx context.Context) hexutil.Bytes {
return hexutil.Bytes(s.ipldBlock)
}
func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
BlockHash common.Hash
Contract common.Address
Slot common.Hash
}) (*StorageResult, error) {
cid, ipldBlock, rlpValue, err := r.backend.IPLDRetriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(args.Contract, args.Slot, args.BlockHash)
if err != nil {
if err == sql.ErrNoRows {
ret := StorageResult{value: []byte{}, cid: "", ipldBlock: []byte{}}
return &ret, nil
}
return nil, err
}
var value interface{}
err = rlp.DecodeBytes(rlpValue, &value)
if err != nil {
return nil, err
}
ret := StorageResult{value: value.([]byte), cid: cid, ipldBlock: ipldBlock}
return &ret, nil
}
func (r *Resolver) GetLogs(ctx context.Context, args struct {
BlockHash common.Hash
Contract *common.Address
}) (*[]*Log, error) {
ret := make([]*Log, 0, 10)
receiptCIDs, receiptsBytes, txs, err := r.backend.IPLDRetriever.RetrieveReceiptsByBlockHash(args.BlockHash)
if err != nil {
return nil, err
}
var logIndexInBlock uint = 0
receipts := make(types.Receipts, len(receiptsBytes))
for index, receiptBytes := range receiptsBytes {
receiptCID := receiptCIDs[index]
receipt := new(types.Receipt)
if err := rlp.DecodeBytes(receiptBytes, receipt); err != nil {
return nil, err
}
receipts[index] = receipt
for _, log := range receipt.Logs {
log.Index = logIndexInBlock
logIndexInBlock++
if args.Contract == nil || *args.Contract == log.Address {
ret = append(ret, &Log{
backend: r.backend,
log: log,
cid: receiptCID,
ipldBlock: receiptBytes,
transaction: &Transaction{
hash: txs[index],
},
})
}
}
}
return &ret, nil
}
+201 -2
View File
@@ -17,15 +17,214 @@
package graphql_test
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"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/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/statediff"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
"github.com/vulcanize/ipld-eth-indexer/pkg/shared"
"github.com/vulcanize/ipld-eth-server/pkg/eth"
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
"github.com/vulcanize/ipld-eth-server/pkg/graphql"
)
var _ = Describe("GraphQL", func() {
It("Builds the schema and creates a new handler", func() {
_, err := graphql.NewHandler(nil)
const (
gqlEndPoint = "127.0.0.1:8083"
)
var (
randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
randomHash = crypto.Keccak256Hash(randomAddr.Bytes())
blocks []*types.Block
receipts []types.Receipts
chain *core.BlockChain
db *postgres.DB
blockHashes []common.Hash
backend *eth.Backend
graphQLServer *graphql.Service
chainConfig = params.TestChainConfig
mockTD = big.NewInt(1337)
client = graphql.NewClient(fmt.Sprintf("http://%s/graphql", gqlEndPoint))
ctx = context.Background()
blockHash common.Hash
contractAddress common.Address
)
It("test init", func() {
var err error
db, err = shared.SetupDB()
Expect(err).ToNot(HaveOccurred())
transformer := eth2.NewStateDiffTransformer(chainConfig, db)
backend, err = eth.NewEthBackend(db, &eth.Config{
ChainConfig: chainConfig,
VmConfig: vm.Config{},
RPCGasCap: big.NewInt(10000000000),
})
Expect(err).ToNot(HaveOccurred())
// make the test blockchain (and state)
blocks, receipts, chain = test_helpers.MakeChain(5, test_helpers.Genesis, test_helpers.TestChainGen)
params := statediff.Params{
IntermediateStateNodes: true,
IntermediateStorageNodes: true,
}
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
builder := statediff.NewBuilder(chain.StateCache())
for i, block := range blocks {
blockHashes = append(blockHashes, block.Hash())
var args statediff.Args
var rcts types.Receipts
if i == 0 {
args = statediff.Args{
OldStateRoot: common.Hash{},
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
} else {
args = statediff.Args{
OldStateRoot: blocks[i-1].Root(),
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
rcts = receipts[i-1]
}
var diff statediff.StateObject
diff, err = builder.BuildStateDiffObject(args, params)
Expect(err).ToNot(HaveOccurred())
diffRlp, err := rlp.EncodeToBytes(diff)
Expect(err).ToNot(HaveOccurred())
blockRlp, err := rlp.EncodeToBytes(block)
Expect(err).ToNot(HaveOccurred())
receiptsRlp, err := rlp.EncodeToBytes(rcts)
Expect(err).ToNot(HaveOccurred())
payload := statediff.Payload{
StateObjectRlp: diffRlp,
BlockRlp: blockRlp,
ReceiptsRlp: receiptsRlp,
TotalDifficulty: mockTD,
}
_, err = transformer.Transform(0, payload)
Expect(err).ToNot(HaveOccurred())
}
// Insert some non-canonical data into the database so that we test our ability to discern canonicity
indexAndPublisher := eth2.NewIPLDPublisher(db)
blockHash = test_helpers.MockBlock.Hash()
contractAddress = test_helpers.ContractAddr
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
Expect(err).ToNot(HaveOccurred())
// The non-canonical header has a child
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayloadForChild)
Expect(err).ToNot(HaveOccurred())
err = publishCode(db, test_helpers.ContractCodeHash, test_helpers.ContractCode)
Expect(err).ToNot(HaveOccurred())
graphQLServer, err = graphql.New(backend, gqlEndPoint, nil, []string{"*"}, rpc.HTTPTimeouts{})
Expect(err).ToNot(HaveOccurred())
err = graphQLServer.Start(nil)
Expect(err).ToNot(HaveOccurred())
})
defer It("test teardown", func() {
err := graphQLServer.Stop()
Expect(err).ToNot(HaveOccurred())
eth.TearDownDB(db)
chain.Stop()
})
Describe("eth_getLogs", func() {
It("Retrieves logs that matches the provided blockHash and contract address", func() {
logs, err := client.GetLogs(ctx, blockHash, contractAddress)
Expect(err).ToNot(HaveOccurred())
expectedLogs := []graphql.LogResponse{
{
Topics: test_helpers.MockLog1.Topics,
Data: hexutil.Bytes(test_helpers.MockLog1.Data),
Transaction: graphql.TransactionResp{Hash: test_helpers.MockTransactions[0].Hash()},
},
}
Expect(logs).To(Equal(expectedLogs))
})
It("Retrieves logs with random hash", func() {
logs, err := client.GetLogs(ctx, randomHash, contractAddress)
Expect(err).ToNot(HaveOccurred())
Expect(len(logs)).To(Equal(0))
})
})
Describe("eth_getStorageAt", func() {
It("Retrieves the storage value at the provided contract address and storage leaf key at the block with the provided hash", func() {
storageRes, err := client.GetStorageAt(ctx, blockHashes[2], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.HexToHash("01")))
storageRes, err = client.GetStorageAt(ctx, blockHashes[3], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.HexToHash("03")))
storageRes, err = client.GetStorageAt(ctx, blockHashes[4], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.HexToHash("09")))
})
It("Retrieves empty data if it tries to access a contract at a blockHash which does not exist", func() {
storageRes, err := client.GetStorageAt(ctx, blockHashes[0], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.Hash{}))
storageRes, err = client.GetStorageAt(ctx, blockHashes[1], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.Hash{}))
})
It("Retrieves empty data if it tries to access a contract slot which does not exist", func() {
storageRes, err := client.GetStorageAt(ctx, blockHashes[3], contractAddress, randomHash.Hex())
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.Hash{}))
})
})
})
func publishCode(db *postgres.DB, codeHash common.Hash, code []byte) error {
tx, err := db.Beginx()
if err != nil {
return err
}
mhKey, err := shared.MultihashKeyFromKeccak256(codeHash)
if err != nil {
_ = tx.Rollback()
return err
}
if err := shared.PublishDirect(tx, mhKey, code); err != nil {
_ = tx.Rollback()
return err
}
return tx.Commit()
}
+27 -1
View File
@@ -65,7 +65,13 @@ const schema string = `
# Data is unindexed data for this log.
data: Bytes!
# Transaction is the transaction that generated this log entry.
transaction: Transaction!
transaction: Transaction
# CID for the Receipt IPLD block this Log exists in.
cid: String!
# IPLD block data for the Receipt this Log exists in.
ipldBlock: Bytes!
}
# Transaction is an Ethereum transaction.
@@ -259,16 +265,36 @@ const schema string = `
topics: [[Bytes32!]!]
}
# Storage trie value with IPLD data.
type StorageResult {
value: Bytes32!
# CID for the storage trie IPLD block.
cid: String!
# Storage trie IPLD block.
ipldBlock: Bytes!
}
type Query {
# Block fetches an Ethereum block by number or by hash. If neither is
# supplied, the most recent known block is returned.
block(number: Long, hash: Bytes32): Block
# Blocks returns all the blocks between two numbers, inclusive. If
# to is not supplied, it defaults to the most recent known block.
blocks(from: Long!, to: Long): [Block!]!
# Transaction returns a transaction specified by its hash.
transaction(hash: Bytes32!): Transaction
# Logs returns log entries matching the provided filter.
logs(filter: FilterCriteria!): [Log!]!
# Get storage slot by block hash and contract address.
getStorageAt(blockHash: Bytes32!, contract: Address!, slot: Bytes32!): StorageResult
# Get contract logs by block hash and contract address.
getLogs(blockHash: Bytes32!, contract: Address): [Log!]
}
`
+1 -1
View File
@@ -41,7 +41,7 @@ func StartHTTPEndpoint(endpoint string, apis []rpc.API, modules []string, cors [
utils.Fatalf("Could not start RPC api: %v", err)
}
extapiURL := fmt.Sprintf("http://%v/", addr)
log.Info("HTTP endpoint opened", "url", extapiURL)
log.Infof("HTTP endpoint opened %s", extapiURL)
return srv, err
}
+99 -25
View File
@@ -17,6 +17,7 @@
package serve
import (
"errors"
"fmt"
"math/big"
"os"
@@ -54,14 +55,31 @@ const (
// Config struct
type Config struct {
DB *postgres.DB
DBConfig postgres.Config
WSEndpoint string
HTTPEndpoint string
IPCEndpoint string
DB *postgres.DB
DBConfig postgres.Config
WSEnabled bool
WSEndpoint string
HTTPEnabled bool
HTTPEndpoint string
IPCEnabled bool
IPCEndpoint string
EthGraphqlEnabled bool
EthGraphqlEndpoint string
IpldGraphqlEnabled bool
IpldGraphqlEndpoint string
IpldPostgraphileEndpoint string
TracingHttpEndpoint string
TracingPostgraphileEndpoint string
ChainConfig *params.ChainConfig
DefaultSender *common.Address
RPCGasCap *big.Int
EthHttpEndpoint string
Client *rpc.Client
SupportStateDiff bool
}
@@ -71,9 +89,6 @@ type Config struct {
func NewConfig() (*Config, error) {
c := new(Config)
viper.BindEnv("server.wsPath", SERVER_WS_PATH)
viper.BindEnv("server.ipcPath", SERVER_IPC_PATH)
viper.BindEnv("server.httpPath", SERVER_HTTP_PATH)
viper.BindEnv("ethereum.httpPath", shared.ETH_HTTP_PATH)
viper.BindEnv("ethereum.defaultSender", ETH_DEFAULT_SENDER_ADDR)
viper.BindEnv("ethereum.rpcGasCap", ETH_RPC_GAS_CAP)
@@ -83,32 +98,91 @@ func NewConfig() (*Config, error) {
c.DBConfig.Init()
ethHTTP := viper.GetString("ethereum.httpPath")
nodeInfo, cli, err := shared.GetEthNodeAndClient(fmt.Sprintf("http://%s", ethHTTP))
ethHTTPEndpoint := fmt.Sprintf("http://%s", ethHTTP)
nodeInfo, cli, err := shared.GetEthNodeAndClient(ethHTTPEndpoint)
if err != nil {
return nil, err
}
c.Client = cli
c.SupportStateDiff = viper.GetBool("ethereum.supportsStateDiff")
c.EthHttpEndpoint = ethHTTPEndpoint
wsPath := viper.GetString("server.wsPath")
if wsPath == "" {
wsPath = "127.0.0.1:8080"
}
c.WSEndpoint = wsPath
ipcPath := viper.GetString("server.ipcPath")
if ipcPath == "" {
home, err := os.UserHomeDir()
if err != nil {
return nil, err
// websocket server
wsEnabled := viper.GetBool("eth.server.ws")
if wsEnabled {
wsPath := viper.GetString("eth.server.wsPath")
if wsPath == "" {
wsPath = "127.0.0.1:8080"
}
ipcPath = filepath.Join(home, ".vulcanize/vulcanize.ipc")
c.WSEndpoint = wsPath
}
c.IPCEndpoint = ipcPath
httpPath := viper.GetString("server.httpPath")
if httpPath == "" {
httpPath = "127.0.0.1:8081"
c.WSEnabled = wsEnabled
// ipc server
ipcEnabled := viper.GetBool("eth.server.ipc")
if ipcEnabled {
ipcPath := viper.GetString("eth.server.ipcPath")
if ipcPath == "" {
home, err := os.UserHomeDir()
if err != nil {
return nil, err
}
ipcPath = filepath.Join(home, ".vulcanize/vulcanize.ipc")
}
c.IPCEndpoint = ipcPath
}
c.HTTPEndpoint = httpPath
c.IPCEnabled = ipcEnabled
// http server
httpEnabled := viper.GetBool("eth.server.http")
if httpEnabled {
httpPath := viper.GetString("eth.server.httpPath")
if httpPath == "" {
httpPath = "127.0.0.1:8081"
}
c.HTTPEndpoint = httpPath
}
c.HTTPEnabled = httpEnabled
// eth graphql endpoint
ethGraphqlEnabled := viper.GetBool("eth.server.graphql")
if ethGraphqlEnabled {
ethGraphqlPath := viper.GetString("eth.server.graphqlPath")
if ethGraphqlPath == "" {
ethGraphqlPath = "127.0.0.1:8082"
}
c.EthGraphqlEndpoint = ethGraphqlPath
}
c.EthGraphqlEnabled = ethGraphqlEnabled
// ipld graphql endpoint
ipldGraphqlEnabled := viper.GetBool("ipld.server.graphql")
if ipldGraphqlEnabled {
ipldGraphqlPath := viper.GetString("ipld.server.graphqlPath")
if ipldGraphqlPath == "" {
ipldGraphqlPath = "127.0.0.1:8083"
}
c.IpldGraphqlEndpoint = ipldGraphqlPath
ipldPostgraphilePath := viper.GetString("ipld.postgraphilePath")
if ipldPostgraphilePath == "" {
return nil, errors.New("ipld-postgraphile-path parameter is empty")
}
c.IpldPostgraphileEndpoint = ipldPostgraphilePath
tracingHttpEndpoint := viper.GetString("tracing.httpPath")
tracingPostgraphilePath := viper.GetString("tracing.postgraphilePath")
// these two parameters either can be both empty or both set
if (tracingHttpEndpoint == "" && tracingPostgraphilePath != "") || (tracingHttpEndpoint != "" && tracingPostgraphilePath == "") {
return nil, errors.New("tracing.httpPath and tracing.postgraphilePath parameters either can be both empty or both set")
}
c.TracingHttpEndpoint = tracingHttpEndpoint
c.TracingPostgraphileEndpoint = tracingPostgraphilePath
}
c.IpldGraphqlEnabled = ipldGraphqlEnabled
overrideDBConnConfig(&c.DBConfig)
serveDB := utils.LoadPostgres(c.DBConfig, nodeInfo, false)
prom.RegisterDBCollector(c.DBConfig.Name, serveDB.DB)
+21
View 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
View 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
View 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
View File
@@ -0,0 +1,3 @@
node_modules
artifacts
cache
+5
View File
@@ -0,0 +1,5 @@
node_modules
#Hardhat files
cache
artifacts
+14
View 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
View 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
View 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
View File
File diff suppressed because it is too large Load Diff
+27
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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))
}
+1 -1
View File
@@ -21,7 +21,7 @@ import "fmt"
const (
Major = 0 // Major version component of the current release
Minor = 3 // Minor version component of the current release
Patch = 5 // Patch version component of the current release
Patch = 6 // Patch version component of the current release
Meta = "alpha" // Version metadata to append to the version string
)