Skip to content

Commit

Permalink
Add /eth/v1/beacon/deposit_snapshot endpoint (#13514)
Browse files Browse the repository at this point in the history
* Add endpoint

* Uncomment in InitializeRoutes

* Add test

* Add 404

* Add more checks

* Test improvements

* Ssz

* Add ssz tags

* Add DepositSnapshot to bazel

* Fix tests

* Fix max size

* Resolve conflicts

* Revert untouched code

* Fix test + review

* Lint

* Oops

* Preston + Radek' review

* Only return 3 finalized roots

* Change to deposit contract depth

* Radek' review

* Gaz

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
  • Loading branch information
saolyn and james-prysm authored Feb 7, 2024
1 parent 9d6160e commit 5afb125
Show file tree
Hide file tree
Showing 13 changed files with 492 additions and 131 deletions.
12 changes: 11 additions & 1 deletion api/server/structs/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@prysm//tools/go:def.bzl", "go_library")
load("@prysm//tools/go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
Expand Down Expand Up @@ -38,3 +38,13 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["conversions_test.go"],
embed = [":go_default_library"],
deps = [
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
],
)
14 changes: 14 additions & 0 deletions api/server/structs/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1074,3 +1074,17 @@ func sszBytesToUint256String(b []byte) (string, error) {
}
return bi.String(), nil
}

func DepositSnapshotFromConsensus(ds *eth.DepositSnapshot) *DepositSnapshot {
finalized := make([]string, 0, len(ds.Finalized))
for _, f := range ds.Finalized {
finalized = append(finalized, hexutil.Encode(f))
}
return &DepositSnapshot{
Finalized: finalized,
DepositRoot: hexutil.Encode(ds.DepositRoot),
DepositCount: fmt.Sprintf("%d", ds.DepositCount),
ExecutionBlockHash: hexutil.Encode(ds.ExecutionHash),
ExecutionBlockHeight: fmt.Sprintf("%d", ds.ExecutionDepth),
}
}
26 changes: 26 additions & 0 deletions api/server/structs/conversions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package structs

import (
"testing"

eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)

func TestDepositSnapshotFromConsensus(t *testing.T) {
ds := &eth.DepositSnapshot{
Finalized: [][]byte{{0xde, 0xad, 0xbe, 0xef}, {0xca, 0xfe, 0xba, 0xbe}},
DepositRoot: []byte{0xab, 0xcd},
DepositCount: 12345,
ExecutionHash: []byte{0x12, 0x34},
ExecutionDepth: 67890,
}

res := DepositSnapshotFromConsensus(ds)
require.NotNil(t, res)
require.DeepEqual(t, []string{"0xdeadbeef", "0xcafebabe"}, res.Finalized)
require.Equal(t, "0xabcd", res.DepositRoot)
require.Equal(t, "12345", res.DepositCount)
require.Equal(t, "0x1234", res.ExecutionBlockHash)
require.Equal(t, "67890", res.ExecutionBlockHeight)
}
12 changes: 12 additions & 0 deletions api/server/structs/endpoints_beacon.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,15 @@ type WeakSubjectivityData struct {
WsCheckpoint *Checkpoint `json:"ws_checkpoint"`
StateRoot string `json:"state_root"`
}

type GetDepositSnapshotResponse struct {
Data *DepositSnapshot `json:"data"`
}

type DepositSnapshot struct {
Finalized []string `json:"finalized"`
DepositRoot string `json:"deposit_root"`
DepositCount string `json:"deposit_count"`
ExecutionBlockHash string `json:"execution_block_hash"`
ExecutionBlockHeight string `json:"execution_block_height"`
}
2 changes: 2 additions & 0 deletions beacon-chain/rpc/eth/beacon/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ go_library(
"//api/server:go_default_library",
"//api/server/structs:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/cache/depositsnapshot:go_default_library",
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/feed:go_default_library",
Expand Down Expand Up @@ -77,6 +78,7 @@ go_test(
"//api/server:go_default_library",
"//api/server/structs:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/cache/depositsnapshot:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition:go_default_library",
Expand Down
46 changes: 46 additions & 0 deletions beacon-chain/rpc/eth/beacon/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api"
"github.com/prysmaticlabs/prysm/v4/api/server/structs"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache/depositsnapshot"
corehelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/filters"
Expand Down Expand Up @@ -2071,3 +2072,48 @@ func (s *Server) GetGenesis(w http.ResponseWriter, r *http.Request) {
}
httputil.WriteJson(w, resp)
}

// GetDepositSnapshot retrieves the EIP-4881 Deposit Tree Snapshot. Either a JSON or,
// if the Accept header was added, bytes serialized by SSZ will be returned.
func (s *Server) GetDepositSnapshot(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetDepositSnapshot")
defer span.End()

if s.BeaconDB == nil {
httputil.HandleError(w, "Could not retrieve beaconDB", http.StatusInternalServerError)
return
}
eth1data, err := s.BeaconDB.ExecutionChainData(ctx)
if err != nil {
httputil.HandleError(w, "Could not retrieve execution chain data: "+err.Error(), http.StatusInternalServerError)
return
}
if eth1data == nil {
httputil.HandleError(w, "Could not retrieve execution chain data: empty Eth1Data", http.StatusInternalServerError)
return
}
snapshot := eth1data.DepositSnapshot
if snapshot == nil || len(snapshot.Finalized) == 0 {
httputil.HandleError(w, "No Finalized Snapshot Available", http.StatusNotFound)
return
}
if len(snapshot.Finalized) > depositsnapshot.DepositContractDepth {
httputil.HandleError(w, "Retrieved invalid deposit snapshot", http.StatusInternalServerError)
return
}
if httputil.RespondWithSsz(r) {
sszData, err := snapshot.MarshalSSZ()
if err != nil {
httputil.HandleError(w, "Could not marshal deposit snapshot into SSZ: "+err.Error(), http.StatusInternalServerError)
return
}
httputil.WriteSsz(w, sszData, "deposit_snapshot.ssz")
return
}
httputil.WriteJson(
w,
&structs.GetDepositSnapshotResponse{
Data: structs.DepositSnapshotFromConsensus(snapshot),
},
)
}
62 changes: 62 additions & 0 deletions beacon-chain/rpc/eth/beacon/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/api"
"github.com/prysmaticlabs/prysm/v4/api/server/structs"
chainMock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache/depositsnapshot"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
Expand Down Expand Up @@ -3617,3 +3618,64 @@ func TestGetGenesis(t *testing.T) {
assert.StringContains(t, "Chain genesis info is not yet known", e.Message)
})
}

func TestGetDepositSnapshot(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
mockTrie := depositsnapshot.NewDepositTree()
deposits := [][32]byte{
bytesutil.ToBytes32([]byte{1}),
bytesutil.ToBytes32([]byte{2}),
bytesutil.ToBytes32([]byte{3}),
}
finalized := 2
for _, leaf := range deposits {
err := mockTrie.Insert(leaf[:], 0)
require.NoError(t, err)
}
err := mockTrie.Finalize(1, deposits[1], 1)
require.NoError(t, err)
err = mockTrie.Finalize(2, deposits[2], 2)
require.NoError(t, err)

snapshot, err := mockTrie.GetSnapshot()
require.NoError(t, err)
root, err := snapshot.CalculateRoot()
require.NoError(t, err)
chainData := &eth.ETH1ChainData{
DepositSnapshot: snapshot.ToProto(),
}
err = beaconDB.SaveExecutionChainData(context.Background(), chainData)
require.NoError(t, err)
s := Server{
BeaconDB: beaconDB,
}

request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/deposit_snapshot", nil)
writer := httptest.NewRecorder()
t.Run("JSON response", func(t *testing.T) {
writer.Body = &bytes.Buffer{}
s.GetDepositSnapshot(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &structs.GetDepositSnapshotResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.NotNil(t, resp.Data)

assert.Equal(t, hexutil.Encode(root[:]), resp.Data.DepositRoot)
assert.Equal(t, hexutil.Encode(deposits[2][:]), resp.Data.ExecutionBlockHash)
assert.Equal(t, strconv.Itoa(mockTrie.NumOfItems()), resp.Data.DepositCount)
assert.Equal(t, finalized, len(resp.Data.Finalized))
})
t.Run("SSZ response", func(t *testing.T) {
writer.Body = &bytes.Buffer{}
request.Header.Set("Accept", api.OctetStreamMediaType)
s.GetDepositSnapshot(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &eth.DepositSnapshot{}
require.NoError(t, resp.UnmarshalSSZ(writer.Body.Bytes()))

assert.Equal(t, hexutil.Encode(root[:]), hexutil.Encode(resp.DepositRoot))
assert.Equal(t, hexutil.Encode(deposits[2][:]), hexutil.Encode(resp.ExecutionHash))
assert.Equal(t, uint64(mockTrie.NumOfItems()), resp.DepositCount)
assert.Equal(t, finalized, len(resp.Finalized))
})
}
1 change: 1 addition & 0 deletions beacon-chain/rpc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ func (s *Service) initializeBeaconServerRoutes(beaconServer *beacon.Server) {
s.cfg.Router.HandleFunc("/eth/v1/beacon/blocks/{block_id}/attestations", beaconServer.GetBlockAttestations).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/blinded_blocks/{block_id}", beaconServer.GetBlindedBlock).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/blocks/{block_id}/root", beaconServer.GetBlockRoot).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/deposit_snapshot", beaconServer.GetDepositSnapshot).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/attestations", beaconServer.ListAttestations).Methods(http.MethodGet)
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/attestations", beaconServer.SubmitAttestations).Methods(http.MethodPost)
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/voluntary_exits", beaconServer.ListVoluntaryExits).Methods(http.MethodGet)
Expand Down
28 changes: 14 additions & 14 deletions beacon-chain/rpc/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,20 @@ func TestServer_InitializeRoutes(t *testing.T) {
"/eth/v1/beacon/blocks/{block_id}/attestations": {http.MethodGet},
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
"/eth/v1/beacon/rewards/sync_committee/{block_id}": {http.MethodPost},
//"/eth/v1/beacon/deposit_snapshot": {http.MethodGet}, not implemented
"/eth/v1/beacon/rewards/blocks/{block_id}": {http.MethodGet},
"/eth/v1/beacon/rewards/attestations/{epoch}": {http.MethodPost},
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},
"/eth/v1/beacon/light_client/bootstrap/{block_root}": {http.MethodGet},
"/eth/v1/beacon/light_client/updates": {http.MethodGet},
"/eth/v1/beacon/light_client/finality_update": {http.MethodGet},
"/eth/v1/beacon/light_client/optimistic_update": {http.MethodGet},
"/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/deposit_snapshot": {http.MethodGet},
"/eth/v1/beacon/rewards/blocks/{block_id}": {http.MethodGet},
"/eth/v1/beacon/rewards/attestations/{epoch}": {http.MethodPost},
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},
"/eth/v1/beacon/light_client/bootstrap/{block_root}": {http.MethodGet},
"/eth/v1/beacon/light_client/updates": {http.MethodGet},
"/eth/v1/beacon/light_client/finality_update": {http.MethodGet},
"/eth/v1/beacon/light_client/optimistic_update": {http.MethodGet},
"/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
}

builderRoutes := map[string][]string{
Expand Down
7 changes: 4 additions & 3 deletions proto/prysm/v1alpha1/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ proto_library(
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:descriptor_proto",
"@com_google_protobuf//:empty_proto",
"@com_google_protobuf//:wrappers_proto",
"@com_google_protobuf//:timestamp_proto",
"@com_google_protobuf//:wrappers_proto",
"@googleapis//google/api:annotations_proto",
],
)
Expand Down Expand Up @@ -130,6 +130,7 @@ ssz_gen_marshal(
"BlobSidecar",
"BlobSidecars",
"BlobIdentifier",
"DepositSnapshot",
],
)

Expand All @@ -155,8 +156,8 @@ go_proto_library(
"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
"@org_golang_google_protobuf//types/descriptorpb:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
],
)

Expand All @@ -176,8 +177,8 @@ go_proto_library(
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@googleapis//google/api:annotations_go_proto",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@io_bazel_rules_go//proto/wkt:wrappers_go_proto",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
"@io_bazel_rules_go//proto/wkt:wrappers_go_proto",
"@org_golang_google_protobuf//types/descriptorpb:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
],
Expand Down
Loading

0 comments on commit 5afb125

Please sign in to comment.