diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6320cd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +data \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c6a6d86 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# docker build . -t ghcr.io/reddio-com/eth_logcache:latest +FROM golang:1.22-bookworm as builder + +RUN mkdir /build +COPY . /build +RUN cd /build && go build . + +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y ca-certificates && apt-get clean + +RUN mkdir /data +COPY --from=builder /build/main /logcache + +CMD ["/logcache"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..5a9926f --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# ETH LogCache + +## Usage + +### Start the service using Docker Compose + +``` +docker-compose up -d +``` + +or, you can build and run with environment variables: + +``` +RPC_URL=https://eth-mainnet.reddio.com START_BLOCK=15560257 go run . +``` + +### Send request to ETH LogCache + +```sh +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{ + "method": "eth_getLogs", + "params": [ + { + "fromBlock": "0x0", + "toBlock": "0x120dc53", + "address": "0xb62bcd40a24985f560b5a9745d478791d8f1945c", + "topics": [ + [ + "0xcfb473e6c03f9a29ddaf990e736fa3de5188a0bd85d684f5b6e164ebfbfff5d2" + ] + ] + } + ], + "id": 62, + "jsonrpc": "2.0" + }' \ + http://localhost:3000 +``` + +Response: +```json +{"block_number":[{"id":62,"jsonrpc":"2.0","result":[{"address":"0xb62bcd40a24985f560b5a9745d478791d8f1945c","blockHash":"0xb1c05e3a5f7791b40d9ded2bb67bd2d250f1ccb036dac0f6a046b7ed2d416df0","blockNumber":"0xed6e42","data":"0x0000000000000000000000006b7763b749073e892c83e674c1ec4799d6f339ef","logIndex":"0x151","removed":false,"topics":["0xcfb473e6c03f9a29ddaf990e736fa3de5188a0bd85d684f5b6e164ebfbfff5d2"],"transactionHash":"0x87130dfe52f1eb4ec22261333534ee7ac2c15e5256ffa7a59ae7153119c6cd73","transactionIndex":"0xc9"},{"address":"0xb62bcd40a24985f560b5a9745d478791d8f1945c","blockHash":"0xb1c05e3a5f7791b40d9ded2bb67bd2d250f1ccb036dac0f6a046b7ed2d416df0","blockNumber":"0xed6e42","data":"0x0000000000000000000000006ea99c6fe2c770c2c46ebe03a4855977282e844f","logIndex":"0x153","removed":false,"topics":["0xcfb473e6c03f9a29ddaf990e736fa3de5188a0bd85d684f5b6e164ebfbfff5d2"],"transactionHash":"0xca00caf8ad277bd23597d497e1c77b4308d40e8787d7e0e5204d320f1f3ab31c","transactionIndex":"0xcb"}]}]} +``` + +## License + +MIT \ No newline at end of file diff --git a/TEST_LOG.md b/TEST_LOG.md new file mode 100644 index 0000000..9f6444d --- /dev/null +++ b/TEST_LOG.md @@ -0,0 +1,148 @@ +## Timeout Request + +Origin Request: + +* 0x0 -> 0 +* 0x120dc53 -> 18930771 + +```json +{ + "method": "eth_getLogs", + "params": [ + { + "fromBlock": "0x0", + "toBlock": "0x120dc53", + "address": "0xb62bcd40a24985f560b5a9745d478791d8f1945c", + "topics": [ + [ + "0xcfb473e6c03f9a29ddaf990e736fa3de5188a0bd85d684f5b6e164ebfbfff5d2" + ] + ] + } + ], + "id": 62, + "jsonrpc": "2.0" +} +``` + +Response: + +```json +{ + "jsonrpc": "2.0", + "id": 62, + "result": [ + { + "address": "0xb62bcd40a24985f560b5a9745d478791d8f1945c", + "blockHash": "0xb1c05e3a5f7791b40d9ded2bb67bd2d250f1ccb036dac0f6a046b7ed2d416df0", + "blockNumber": "0xed6e42", + "data": "0x0000000000000000000000006b7763b749073e892c83e674c1ec4799d6f339ef", + "logIndex": "0x151", + "removed": false, + "topics": [ + "0xcfb473e6c03f9a29ddaf990e736fa3de5188a0bd85d684f5b6e164ebfbfff5d2" + ], + "transactionHash": "0x87130dfe52f1eb4ec22261333534ee7ac2c15e5256ffa7a59ae7153119c6cd73", + "transactionIndex": "0xc9" + }, + { + "address": "0xb62bcd40a24985f560b5a9745d478791d8f1945c", + "blockHash": "0xb1c05e3a5f7791b40d9ded2bb67bd2d250f1ccb036dac0f6a046b7ed2d416df0", + "blockNumber": "0xed6e42", + "data": "0x0000000000000000000000006ea99c6fe2c770c2c46ebe03a4855977282e844f", + "logIndex": "0x153", + "removed": false, + "topics": [ + "0xcfb473e6c03f9a29ddaf990e736fa3de5188a0bd85d684f5b6e164ebfbfff5d2" + ], + "transactionHash": "0xca00caf8ad277bd23597d497e1c77b4308d40e8787d7e0e5204d320f1f3ab31c", + "transactionIndex": "0xcb" + } + ] +} +``` + + + +## Verify Request + +Original Request: + +* 0x121AE54 -> 18984532 +* 0x121e2be -> 18997950 + +```json +{ + "method": "eth_getLogs", + "params": [ + { + "fromBlock": "0x121AE54", + "toBlock": "0x121e2be", + "address": "0x15e6e0d4ebeac120f9a97e71faa6a0235b85ed12", + "topics": [ + [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ] + ] + } + ], + "id": 62, + "jsonrpc": "2.0" +} +``` + +Response: + +```json +{ + "jsonrpc": "2.0", + "id": 62, + "result": [ + { + "address": "0x15e6e0d4ebeac120f9a97e71faa6a0235b85ed12", + "blockHash": "0xbca59ccc09a7bb1e6877c943fce30e7d3482cc461769d98f5b7ede1d36e02463", + "blockNumber": "0x121ae54", + "data": "0x0000000000000000000000000000000000000000000000006e2255f409800000", + "logIndex": "0xa9", + "removed": false, + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f328a29acc598ff8a399e45d8b11be22ef6d3987" + ], + "transactionHash": "0xa6a4412d64971843db5babcf25b9f5644acd295be8ff3ebbdc38e61276b713dd", + "transactionIndex": "0x66" + }, + { + "address": "0x15e6e0d4ebeac120f9a97e71faa6a0235b85ed12", + "blockHash": "0xacd6dda9c3030cef969feb8aa46730e7c929b2267b9b4291d680ed964f0a653e", + "blockNumber": "0x121e2be", + "data": "0x0000000000000000000000000000000000000000000000003782dace9d900000", + "logIndex": "0xaa", + "removed": false, + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000f328a29acc598ff8a399e45d8b11be22ef6d3987", + "0x000000000000000000000000861e3c82bc2753ea64ae5f962d993df6853a6700" + ], + "transactionHash": "0x6c42543f5371e174ba751acb41f8ef9f78366fb996646203bffa43b489606bf0", + "transactionIndex": "0x7d" + }, + { + "address": "0x15e6e0d4ebeac120f9a97e71faa6a0235b85ed12", + "blockHash": "0xacd6dda9c3030cef969feb8aa46730e7c929b2267b9b4291d680ed964f0a653e", + "blockNumber": "0x121e2be", + "data": "0x0000000000000000000000000000000000000000000000003782dace9d900000", + "logIndex": "0xab", + "removed": false, + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000861e3c82bc2753ea64ae5f962d993df6853a6700", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "transactionHash": "0x6c42543f5371e174ba751acb41f8ef9f78366fb996646203bffa43b489606bf0", + "transactionIndex": "0x7d" + } + ] +} +``` \ No newline at end of file diff --git a/chain.go b/chain.go new file mode 100644 index 0000000..59bac53 --- /dev/null +++ b/chain.go @@ -0,0 +1,45 @@ +package main + +import ( + "context" + "fmt" + "math/big" +) + +func LatestBlockOnChain() (uint64, error) { + header, err := ETH_CLIENT.HeaderByNumber(context.Background(), nil) + if err != nil { + return 0, fmt.Errorf("Failed to get latest block header: %v", err) + } + + return header.Number.Uint64(), nil +} + +func getTopicListByBlockNumber(blockNumber uint64) ([]string, error) { + blockNumberBig := new(big.Int) + blockNumberBig.SetUint64(blockNumber) + block, err := ETH_CLIENT.BlockByNumber(context.Background(), blockNumberBig) + for err != nil { + fmt.Printf("Failed to get block: %v\n", err) + block, err = ETH_CLIENT.BlockByNumber(context.Background(), blockNumberBig) + } + + totalTopics := make([]string, 0) + + for _, tx := range block.Transactions() { + // fmt.Printf("Dealing with transaction: %s\n", tx.Hash().Hex()) + + receipt, err := ETH_CLIENT.TransactionReceipt(context.Background(), tx.Hash()) + if err != nil { + return nil, fmt.Errorf("Failed to get transaction receipt: %v", err) + } + + for _, log := range receipt.Logs { + for _, topic := range log.Topics { + totalTopics = append(totalTopics, topic.Hex()) + } + } + } + + return totalTopics, nil +} diff --git a/db.go b/db.go new file mode 100644 index 0000000..c1a9f37 --- /dev/null +++ b/db.go @@ -0,0 +1,133 @@ +package main + +import ( + "encoding/binary" + "fmt" + + "github.com/cockroachdb/pebble" +) + +var ( + DB *pebble.DB +) + +func Uint64ArrayToByteSlice(array []uint64) []byte { + byteSlice := make([]byte, 8*len(array)) + for i, v := range array { + binary.LittleEndian.PutUint64(byteSlice[i*8:], v) + } + return byteSlice +} + +func ByteSliceToUint64Array(byteSlice []byte) []uint64 { + array := make([]uint64, len(byteSlice)/8) + for i := range array { + array[i] = binary.LittleEndian.Uint64(byteSlice[i*8:]) + } + return array +} + +func DeduplicateUint64Array(array []uint64) []uint64 { + keys := make(map[uint64]bool) + list := []uint64{} + for _, entry := range array { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +func SetKeyNumberArray(key string, number uint64) error { + // Check if key exists, if exists, read array and append number to array + // If not exists, create array with number + keyByteSlice := []byte(key) + existingNumberArray, err := GetKeyNumberArray(key) + if err != nil { + return err + } + if existingNumberArray != nil { + // Append number to array + existingNumberArray = append(existingNumberArray, number) + + // Deduplicate array + existingNumberArray = DeduplicateUint64Array(existingNumberArray) + + numberByteSlice := Uint64ArrayToByteSlice(existingNumberArray) + err := DB.Set(keyByteSlice, numberByteSlice, pebble.NoSync) + if err != nil { + return err + } + return nil + } else { + // Create array with number + numberArray := []uint64{number} + numberByteSlice := Uint64ArrayToByteSlice(numberArray) + err := DB.Set(keyByteSlice, numberByteSlice, pebble.NoSync) + if err != nil { + return err + } + return nil + } +} + +func SetKeyNumber(key string, number uint64) error { + keyByteSlice := []byte(key) + value := make([]byte, 8) + binary.LittleEndian.PutUint64(value, uint64(number)) + err := DB.Set(keyByteSlice, value, pebble.Sync) + if err != nil { + return err + } + return nil +} + +func GetKeyNumberArray(key string) ([]uint64, error) { + keyByteSlice := []byte(key) + value, closer, err := DB.Get(keyByteSlice) + if err != nil { + // Check if key is not found + if err == pebble.ErrNotFound { + return nil, nil + } + return nil, err + } + defer closer.Close() + + numberArray := ByteSliceToUint64Array(value) + return numberArray, nil +} + +func GetKeyNumber(key string) (uint64, error) { + keyByteSlice := []byte(key) + value, closer, err := DB.Get(keyByteSlice) + if err != nil { + // Check if key is not found + if err == pebble.ErrNotFound { + fmt.Printf("Key %s not found\n", key) + return 0, nil + } + } + defer closer.Close() + + number := binary.LittleEndian.Uint64(value) + return number, nil +} + +func FlushDB() error { + err := DB.Flush() + if err != nil { + return err + } + return nil +} + +func LatestBlockNumberInDB() (uint64, error) { + key := "LatestBlockNumberInDB" + number, err := GetKeyNumber(key) + if err != nil { + return 0, err + } + return number, nil +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e1cb861 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3' + +services: + webp: + image: ghcr.io/reddio-com/eth_logcache:latest + restart: always + environment: + - RPC_URL=https://eth-mainnet.reddio.com + - START_BLOCK=15560257 + volumes: + - ./data:/data + ports: + - 3000:3000 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ec8a2c3 --- /dev/null +++ b/go.mod @@ -0,0 +1,66 @@ +module main + +go 1.22 + +require ( + github.com/cockroachdb/pebble v1.0.0 + github.com/ethereum/go-ethereum v1.13.11 + github.com/gofiber/fiber/v2 v2.52.0 +) + +require ( + github.com/DataDog/zstd v1.5.5 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/getsentry/sentry-go v0.26.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.46.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.17.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1e21819 --- /dev/null +++ b/go.sum @@ -0,0 +1,234 @@ +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= +github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.0.0 h1:WZWlV/s78glZbY2ylUITDOWSVBD3cLjcWPLRPFbHNYg= +github.com/cockroachdb/pebble v1.0.0/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.11 h1:b51Dsm+rEg7anFRUMGB8hODXHvNfcRKzz9vcj8wSdUs= +github.com/ethereum/go-ethereum v1.13.11/go.mod h1:gFtlVORuUcT+UUIcJ/veCNjkuOSujCi338uSHJrYAew= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/getsentry/sentry-go v0.26.0 h1:IX3++sF6/4B5JcevhdZfdKIHfyvMmAq/UnqcyT2H6mA= +github.com/getsentry/sentry-go v0.26.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE= +github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= +github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..6ce3f4b --- /dev/null +++ b/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "strconv" + "time" + + "github.com/cockroachdb/pebble" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/gofiber/fiber/v2" +) + +var ( + RPC_URL = "https://eth-mainnet.reddio.com" + ETH_CLIENT, _ = ethclient.Dial(RPC_URL) + err error + START_BLOCK = uint64(1) + HTTP_CLIENT = &http.Client{ + Timeout: 10 * time.Second, + } +) + +func init() { + + // Read from ENV for override + if os.Getenv("RPC_URL") != "" { + RPC_URL = os.Getenv("RPC_URL") + } + // Read from ENV for override + if os.Getenv("START_BLOCK") != "" { + START_BLOCK_STR := os.Getenv("START_BLOCK") + START_BLOCK_INT, _ := strconv.Atoi(START_BLOCK_STR) + START_BLOCK = uint64(START_BLOCK_INT) + } + + DB, err = pebble.Open("data", &pebble.Options{}) + if err != nil { + log.Fatal(err) + } + + // Check if LatestBlockNumberInDB exists, if not, create it + LatestBlockNumberInDBKey, err := LatestBlockNumberInDB() + if err != nil { + log.Fatal(err) + } + if LatestBlockNumberInDBKey == 0 { + fmt.Println("LatestBlockNumberInDB not found, creating it") + SetKeyNumber("LatestBlockNumberInDB", START_BLOCK) + } +} + +func main() { + // Init runner + go runner() + + // Init API + app := fiber.New() + + app.Post("/", payload_handler) + app.Get("/latest_block", get_latest_block) + app.Get("/get_block_number", get_block_number) + + app.Listen(":3000") +} diff --git a/router.go b/router.go new file mode 100644 index 0000000..2aafa63 --- /dev/null +++ b/router.go @@ -0,0 +1,211 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "sort" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type Payload struct { + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + ID int `json:"id"` + Params []Params `json:"params"` +} + +type Params struct { + Topics []TopicList `json:"topics"` + FromBlock string `json:"fromBlock"` + ToBlock string `json:"toBlock"` + Address string `json:"address"` +} + +type TopicList []string + +// Given [1,2,3,5,6,7], return [[1,3],[5,7]] +func getListOfStartAndToBlockNumber(blockNumberList []uint64) [][]uint64 { + startBlockNumber := blockNumberList[0] + toBlockNumber := blockNumberList[0] + blockNumberRangeList := make([][]uint64, 0) + for i := 1; i < len(blockNumberList); i++ { + if blockNumberList[i] == toBlockNumber+1 { + toBlockNumber = blockNumberList[i] + } else { + blockNumberRangeList = append(blockNumberRangeList, []uint64{startBlockNumber, toBlockNumber}) + startBlockNumber = blockNumberList[i] + toBlockNumber = blockNumberList[i] + } + } + blockNumberRangeList = append(blockNumberRangeList, []uint64{startBlockNumber, toBlockNumber}) + return blockNumberRangeList +} + +// Payload handler is for user facing handler +// It will check if the method is eth_getLogs and use Pebble to get block_numbers +// Other requests will be forwarded to RPC_URL + +// Example payload +// +// { +// "method": "eth_getLogs", +// "params": [ +// { +// "fromBlock": "0x0", +// "toBlock": "0x1234325", +// "address": "0xb62bcd40a24985f560b5a9745d478791d8f1945c", +// "topics": [ +// [ +// "0xcfb473e6c03f9a29ddaf990e736fa3de5188a0bd85d684f5b6e164ebfbfff5d2" +// ] +// ] +// } +// ], +// "id": 62, +// "jsonrpc": "2.0" +// } +func payload_handler(c *fiber.Ctx) error { + // Check method is eth_getLogs + payload := new(Payload) + if err := c.BodyParser(payload); err != nil { + return fmt.Errorf("Failed to parse body: %v\n", err) + } + if payload.Method == "eth_getLogs" { + // Check if topics is empty + if len(payload.Params[0].Topics) > 0 { + + // TODO: Here only processeed the first topic, need to process all topics + topicList0 := payload.Params[0].Topics[0] + combinedBlockNumberList := make([]uint64, 0) + for _, topic := range topicList0 { + blockNumberArray, err := GetKeyNumberArray(topic) + if err != nil { + return fmt.Errorf("Failed to get block number: %v\n", err) + } + combinedBlockNumberList = append(combinedBlockNumberList, blockNumberArray...) + } + + // Check if empty + if len(combinedBlockNumberList) == 0 { + return c.JSON(fiber.Map{ + "block_number": combinedBlockNumberList, + }) + } + + // Dedup and sort + combinedBlockNumberList = DeduplicateUint64Array(combinedBlockNumberList) + sort.Slice(combinedBlockNumberList, func(i, j int) bool { + return combinedBlockNumberList[i] < combinedBlockNumberList[j] + }) + + // BlockNumber Ranges + blockNumberRanges := getListOfStartAndToBlockNumber(combinedBlockNumberList) + + // Now we have a mixed block number ranges, we do requests to RPC_URL + // Make sure to check if the block number is in the FromBlock and ToBlock + FromBlockDec, err := strconv.ParseUint(payload.Params[0].FromBlock, 0, 64) + if err != nil { + return fmt.Errorf("Failed to parse fromBlock: %v\n", err) + } + ToBlockDec, err := strconv.ParseUint(payload.Params[0].ToBlock, 0, 64) + if err != nil { + return fmt.Errorf("Failed to parse toBlock: %v\n", err) + } + + combinedResponse := make([]map[string]interface{}, 0) + + for _, eachBlockRange := range blockNumberRanges { + + if eachBlockRange[0] < FromBlockDec { + eachBlockRange[0] = FromBlockDec + } + if eachBlockRange[1] > ToBlockDec { + eachBlockRange[1] = ToBlockDec + } + if eachBlockRange[0] > eachBlockRange[1] { + // swap + eachBlockRange[0], eachBlockRange[1] = eachBlockRange[1], eachBlockRange[0] + } + + // Do request to RPC_URL + newPayload := &Payload{ + Jsonrpc: payload.Jsonrpc, + Method: payload.Method, + ID: payload.ID, + Params: []Params{ + { + Topics: payload.Params[0].Topics, + FromBlock: fmt.Sprintf("0x%x", eachBlockRange[0]), + ToBlock: fmt.Sprintf("0x%x", eachBlockRange[1]), + Address: payload.Params[0].Address, + }, + }, + } + fmt.Printf("New payload: %+v\n", newPayload) + payloadByteSlice, err := json.Marshal(newPayload) + if err != nil { + return fmt.Errorf("Failed to marshal payload: %v\n", err) + } + + // Send request to RPC_URL + response, err := HTTP_CLIENT.Post(RPC_URL, "application/json", bytes.NewBuffer(payloadByteSlice)) + if err != nil { + return fmt.Errorf("Failed to send request to RPC_URL: %v\n", err) + } + defer response.Body.Close() + + // Read response + var result map[string]interface{} + json.NewDecoder(response.Body).Decode(&result) + combinedResponse = append(combinedResponse, result) + fmt.Printf("Result: %+v\n", result) + } + + return c.JSON(fiber.Map{ + "block_number": combinedResponse, + }) + } + } + return nil +} + +func get_latest_block(c *fiber.Ctx) error { + LatestBlockNumberInDBKey, err := LatestBlockNumberInDB() + if err != nil { + return fmt.Errorf("Failed to get latest block number in DB: %v\n", err) + } + return c.JSON(fiber.Map{ + "latest_block": LatestBlockNumberInDBKey, + }) +} + +func get_block_number(c *fiber.Ctx) error { + topic := c.Query("topic") + if topic == "" { + return fmt.Errorf("Topic is empty") + } + blockNumberArray, err := GetKeyNumberArray(topic) + if err != nil { + return fmt.Errorf("Failed to get block number: %v\n", err) + } + if len(blockNumberArray) == 0 { + return c.JSON(fiber.Map{ + "block_number": blockNumberArray, + }) + } + // Dedup and sort + blockNumberArray = DeduplicateUint64Array(blockNumberArray) + sort.Slice(blockNumberArray, func(i, j int) bool { + return blockNumberArray[i] < blockNumberArray[j] + }) + + // BlockNumber Ranges + blockNumberRanges := getListOfStartAndToBlockNumber(blockNumberArray) + + return c.JSON(fiber.Map{ + "block_number": blockNumberRanges, + }) +} diff --git a/runner.go b/runner.go new file mode 100644 index 0000000..b665e23 --- /dev/null +++ b/runner.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" +) + +func runner() { + for { + // Get LatestBlockNumberOnChain + LatestBlockNumberOnChain, err := LatestBlockOnChain() + if err != nil { + fmt.Printf("Failed to get latest block number on chain: %v\n", err) + return + } + fmt.Printf("Latest block number on chain: %d\n", LatestBlockNumberOnChain) + // Get Key with LatestBlockNumberInDB + LatestBlockNumberInDBKey, err := LatestBlockNumberInDB() + if err != nil { + fmt.Printf("Failed to get latest block number in DB: %v\n", err) + return + } + + // blockNumber := 19088166 + // value := make([]byte, 8) + // binary.LittleEndian.PutUintgo64(value, uint64(blockNumber)) + // fmt.Println("Value: ", value) + + // Get block number from DB + fmt.Printf("Latest block number in DB: %d\n", LatestBlockNumberInDBKey) + + if LatestBlockNumberInDBKey < LatestBlockNumberOnChain { + for i := LatestBlockNumberInDBKey; i < LatestBlockNumberOnChain; i++ { + fmt.Println("Dealing with block number: ", i) + // Get topic list from block number + topicList, err := getTopicListByBlockNumber(i) + if err != nil { + fmt.Printf("Failed to get topic list: %v\n", err) + // Retry + return + } + fmt.Printf("Total topics: %d\n", len(topicList)) + if len(topicList) > 0 { + for _, topic := range topicList { + SetKeyNumberArray(topic, i) + } + // Flush DB + FlushDB() + } + // Update LatestBlockNumberInDB + SetKeyNumber("LatestBlockNumberInDB", i) + } + } + defer DB.Close() + } +}