Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1.91.1 #133

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions beacon/server_for_op_stack/api_func.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package server_for_op_stack

import (
"context"

Check failure on line 4 in beacon/server_for_op_stack/api_func.go

View workflow job for this annotation

GitHub Actions / golang-lint (1.21.x, ubuntu-latest)

File is not `goimports`-ed (goimports)
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"sort"
)

var (
client *ethclient.Client
)

func Init(nodeURL string) {
var err error
client, err = ethclient.Dial(nodeURL)
if err != nil {
log.Crit("Error connecting to client", "nodeURL", nodeURL, "error", err)
}
}

type BlobSidecar struct {
Blob kzg4844.Blob `json:"blob"`
Index int `json:"index"`
KZGCommitment kzg4844.Commitment `json:"kzg_commitment"`
KZGProof kzg4844.Proof `json:"kzg_proof"`
}

type APIGetBlobSidecarsResponse struct {
Data []*BlobSidecar `json:"data"`
}

type ReducedGenesisData struct {
GenesisTime string `json:"genesis_time"`
}

type APIGenesisResponse struct {
Data ReducedGenesisData `json:"data"`
}

type ReducedConfigData struct {
SecondsPerSlot string `json:"SECONDS_PER_SLOT"`
}

type IndexedBlobHash struct {
Index int // absolute index in the block, a.k.a. position in sidecar blobs array
Hash common.Hash // hash of the blob, used for consistency checks
}

func configSpec() ReducedConfigData {
return ReducedConfigData{SecondsPerSlot: "1"}
}

func beaconGenesis() APIGenesisResponse {
return APIGenesisResponse{Data: ReducedGenesisData{GenesisTime: "0"}}
}

func beaconBlobSidecars(ctx context.Context, slot uint64, indices []int) (APIGetBlobSidecarsResponse, error) {
var blockNrOrHash rpc.BlockNumberOrHash
blockNum, err := fetchBlockNumberByTime(ctx, int64(slot), client)
if err != nil {
log.Error("Error fetching block number", "slot", slot, "indices", indices)
return APIGetBlobSidecarsResponse{}, err
}
rpcBlockNum := rpc.BlockNumber(blockNum)
blockNrOrHash.BlockNumber = &rpcBlockNum
sideCars, err := client.BlobSidecars(ctx, blockNrOrHash)
if err != nil {
log.Error("Error fetching Sidecars", "blockNrOrHash", blockNrOrHash, "err", err)
return APIGetBlobSidecarsResponse{}, err
}
sort.Ints(indices)
fullBlob := len(indices) == 0
res := APIGetBlobSidecarsResponse{}
idx := 0
curIdx := 0
for _, sideCar := range sideCars {
for i := 0; i < len(sideCar.Blobs); i++ {
//hash := kZGToVersionedHash(sideCar.Commitments[i])
if !fullBlob && curIdx >= len(indices) {
break
}
if fullBlob || idx == indices[curIdx] {
res.Data = append(res.Data, &BlobSidecar{
Index: idx,
Blob: sideCar.Blobs[i],
KZGCommitment: sideCar.Commitments[i],
KZGProof: sideCar.Proofs[i],
})
curIdx++
}
idx++
}
}

return res, nil
}
87 changes: 87 additions & 0 deletions beacon/server_for_op_stack/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package server_for_op_stack

import (
"fmt"
"github.com/prysmaticlabs/prysm/v5/api/server/structs"

Check failure on line 5 in beacon/server_for_op_stack/handlers.go

View workflow job for this annotation

GitHub Actions / golang-lint (1.21.x, ubuntu-latest)

File is not `goimports`-ed (goimports)
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/network/httputil"
"net/http"
"net/url"
"strconv"
"strings"
)

var (
versionMethod = "/eth/v1/node/version"
specMethod = "/eth/v1/config/spec"
genesisMethod = "/eth/v1/beacon/genesis"
sidecarsMethodPrefix = "/eth/v1/beacon/blob_sidecars/{slot}"
)

func VersionMethod(w http.ResponseWriter, r *http.Request) {
resp := &structs.GetVersionResponse{
Data: &structs.Version{
Version: "",
},
}
httputil.WriteJson(w, resp)
}

func SpecMethod(w http.ResponseWriter, r *http.Request) {
httputil.WriteJson(w, &structs.GetSpecResponse{Data: configSpec()})
}

func GenesisMethod(w http.ResponseWriter, r *http.Request) {
httputil.WriteJson(w, beaconGenesis())
}

func SidecarsMethod(w http.ResponseWriter, r *http.Request) {
indices, err := parseIndices(r.URL)
if err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
segments := strings.Split(r.URL.Path, "/")
slot, err := strconv.ParseUint(segments[len(segments)-1], 10, 64)
if err != nil {
httputil.HandleError(w, "not a valid slot(timestamp)", http.StatusBadRequest)
return
}

resp, err := beaconBlobSidecars(r.Context(), slot, indices)
if err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
httputil.WriteJson(w, resp)
}

// parseIndices filters out invalid and duplicate blob indices
func parseIndices(url *url.URL) ([]int, error) {
rawIndices := url.Query()["indices"]
indices := make([]int, 0, field_params.MaxBlobsPerBlock)
invalidIndices := make([]string, 0)
loop:
for _, raw := range rawIndices {
ix, err := strconv.Atoi(raw)
if err != nil {
invalidIndices = append(invalidIndices, raw)
continue
}
if ix >= field_params.MaxBlobsPerBlock {
invalidIndices = append(invalidIndices, raw)
continue
}
for i := range indices {
if ix == indices[i] {
continue loop
}
}
indices = append(indices, ix)
}

if len(invalidIndices) > 0 {
return nil, fmt.Errorf("requested blob indices %v are invalid", invalidIndices)
}
return indices, nil
}
73 changes: 73 additions & 0 deletions beacon/server_for_op_stack/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package server_for_op_stack

import (
"context"
"net/http"

"github.com/gorilla/mux"
"github.com/prysmaticlabs/prysm/v5/api/server"
)

type Config struct {
BSCNodeURL string
HostPort string
}

type Service struct {
cfg Config
router *mux.Router
}

func NewService(ctx context.Context, cfg *Config) *Service {
Init(cfg.BSCNodeURL)
router := newRouter()

return &Service{
cfg: *cfg,
router: router,
}
}

func (s *Service) Run() {
_ = http.ListenAndServe(s.cfg.HostPort, s.router)
}

func newRouter() *mux.Router {
r := mux.NewRouter()
r.Use(server.NormalizeQueryValuesHandler)
for _, e := range endpoints() {
r.HandleFunc(e.path, e.handler).Methods(e.methods...)
}
return r
}

type endpoint struct {
path string
handler http.HandlerFunc
methods []string
}

func endpoints() []endpoint {
return []endpoint{
{
path: versionMethod,
handler: VersionMethod,
methods: []string{http.MethodGet},
},
{
path: specMethod,
handler: SpecMethod,
methods: []string{http.MethodGet},
},
{
path: genesisMethod,
handler: GenesisMethod,
methods: []string{http.MethodGet},
},
{
path: sidecarsMethodPrefix,
handler: SidecarsMethod,
methods: []string{http.MethodGet},
},
}
}
95 changes: 95 additions & 0 deletions beacon/server_for_op_stack/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package server_for_op_stack

import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"strconv"
"testing"
)

func init() {
Init("https://bsc-mainnet.nodereal.io/v1/{api-key}")
}

func TestFetchBlockNumberByTime(t *testing.T) {
blockNum, err := fetchBlockNumberByTime(context.Background(), 1724052941, client)
assert.Nil(t, err)
assert.Equal(t, uint64(41493946), blockNum)

blockNum, err = fetchBlockNumberByTime(context.Background(), 1734052941, client)

Check failure on line 22 in beacon/server_for_op_stack/server_test.go

View workflow job for this annotation

GitHub Actions / golang-lint (1.21.x, ubuntu-latest)

ineffectual assignment to blockNum (ineffassign)
assert.Equal(t, err, errors.New("time too large"))

blockNum, err = fetchBlockNumberByTime(context.Background(), 1600153618, client)
assert.Nil(t, err)
assert.Equal(t, uint64(493946), blockNum)
}

func TestBeaconBlobSidecars(t *testing.T) {
indexBlobHash := []IndexedBlobHash{
{Hash: common.HexToHash("0x01231952ecbaede62f8d0398b656072c072db36982c9ef106fbbc39ce14f983c"), Index: 0},
{Hash: common.HexToHash("0x012c21a8284d2d707bb5318e874d2e1b97a53d028e96abb702b284a2cbb0f79c"), Index: 1},
{Hash: common.HexToHash("0x011196c8d02536ede0382aa6e9fdba6c460169c0711b5f97fcd701bd8997aee3"), Index: 2},
{Hash: common.HexToHash("0x019c86b46b27401fb978fd175d1eb7dadf4976d6919501b0c5280d13a5bab57b"), Index: 3},
{Hash: common.HexToHash("0x01e00db7ee99176b3fd50aab45b4fae953292334bbf013707aac58c455d98596"), Index: 4},
{Hash: common.HexToHash("0x0117d23b68123d578a98b3e1aa029661e0abda821a98444c21992eb1e5b7208f"), Index: 5},
//{Hash: common.HexToHash("0x01e00db7ee99176b3fd50aab45b4fae953292334bbf013707aac58c455d98596"), Index: 1},
}

resp, err := beaconBlobSidecars(context.Background(), 1724055046, []int{0, 1, 2, 3, 4, 5}) // block: 41494647
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.NotEmpty(t, resp.Data)
for i, sideCar := range resp.Data {
assert.Equal(t, indexBlobHash[i].Index, sideCar.Index)
assert.Equal(t, indexBlobHash[i].Hash, kZGToVersionedHash(sideCar.KZGCommitment))
}

apiscs := make([]*BlobSidecar, 0, len(indexBlobHash))
// filter and order by hashes
for _, h := range indexBlobHash {
for _, apisc := range resp.Data {
if h.Index == int(apisc.Index) {

Check failure on line 54 in beacon/server_for_op_stack/server_test.go

View workflow job for this annotation

GitHub Actions / golang-lint (1.21.x, ubuntu-latest)

unnecessary conversion (unconvert)
apiscs = append(apiscs, apisc)
break
}
}
}

assert.Equal(t, len(apiscs), len(resp.Data))
assert.Equal(t, len(apiscs), len(indexBlobHash))
}

type TimeToSlotFn func(timestamp uint64) (uint64, error)

// GetTimeToSlotFn returns a function that converts a timestamp to a slot number.
func GetTimeToSlotFn(ctx context.Context) (TimeToSlotFn, error) {

Check failure on line 68 in beacon/server_for_op_stack/server_test.go

View workflow job for this annotation

GitHub Actions / golang-lint (1.21.x, ubuntu-latest)

unnecessary leading newline (whitespace)

genesis := beaconGenesis()
config := configSpec()

genesisTime, _ := strconv.ParseUint(genesis.Data.GenesisTime, 10, 64)
secondsPerSlot, _ := strconv.ParseUint(config.SecondsPerSlot, 10, 64)
if secondsPerSlot == 0 {
return nil, fmt.Errorf("got bad value for seconds per slot: %v", config.SecondsPerSlot)
}
timeToSlotFn := func(timestamp uint64) (uint64, error) {
if timestamp < genesisTime {
return 0, fmt.Errorf("provided timestamp (%v) precedes genesis time (%v)", timestamp, genesisTime)
}
return (timestamp - genesisTime) / secondsPerSlot, nil
}
return timeToSlotFn, nil
}

func TestAPI(t *testing.T) {
slotFn, err := GetTimeToSlotFn(context.Background())
assert.Nil(t, err)

expTx := uint64(123151345)
gotTx, err := slotFn(expTx)
assert.Nil(t, err)
assert.Equal(t, expTx, gotTx)
}
Loading
Loading