@@ -4754,3 +4754,196 @@ func Test_validateBlobSidecars(t *testing.T) {
47544754 require .NoError (t , err )
47554755 require .ErrorContains (t , "could not verify blob proof: can't verify opening proof" , s .validateBlobSidecars (b , [][]byte {blob [:]}, [][]byte {proof [:]}))
47564756}
4757+
4758+ func TestGetPendingDeposits (t * testing.T ) {
4759+ st , _ := util .DeterministicGenesisStateElectra (t , 10 )
4760+
4761+ validators := st .Validators ()
4762+ dummySig := make ([]byte , 96 )
4763+ for j := 0 ; j < 96 ; j ++ {
4764+ dummySig [j ] = byte (j )
4765+ }
4766+ deps := make ([]* eth.PendingDeposit , 10 )
4767+ for i := 0 ; i < len (deps ); i += 1 {
4768+ deps [i ] = & eth.PendingDeposit {
4769+ PublicKey : validators [i ].PublicKey ,
4770+ WithdrawalCredentials : validators [i ].WithdrawalCredentials ,
4771+ Amount : 100 ,
4772+ Slot : 0 ,
4773+ Signature : dummySig ,
4774+ }
4775+ }
4776+ require .NoError (t , st .SetPendingDeposits (deps ))
4777+
4778+ chainService := & chainMock.ChainService {
4779+ Optimistic : false ,
4780+ FinalizedRoots : map [[32 ]byte ]bool {},
4781+ }
4782+ server := & Server {
4783+ Stater : & testutil.MockStater {
4784+ BeaconState : st ,
4785+ },
4786+ OptimisticModeFetcher : chainService ,
4787+ FinalizationFetcher : chainService ,
4788+ }
4789+
4790+ t .Run ("json response" , func (t * testing.T ) {
4791+ req := httptest .NewRequest (http .MethodGet , "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits" , nil )
4792+ req .SetPathValue ("state_id" , "head" )
4793+ rec := httptest .NewRecorder ()
4794+ rec .Body = new (bytes.Buffer )
4795+
4796+ server .GetPendingDeposits (rec , req )
4797+ require .Equal (t , http .StatusOK , rec .Code )
4798+ require .Equal (t , "electra" , rec .Header ().Get (api .VersionHeader ))
4799+
4800+ var resp structs.GetPendingDepositsResponse
4801+ require .NoError (t , json .Unmarshal (rec .Body .Bytes (), & resp ))
4802+
4803+ expectedVersion := version .String (st .Version ())
4804+ require .Equal (t , expectedVersion , resp .Version )
4805+
4806+ require .Equal (t , false , resp .ExecutionOptimistic )
4807+ require .Equal (t , false , resp .Finalized )
4808+
4809+ expectedDeposits := structs .PendingDepositsFromConsensus (deps )
4810+ require .DeepEqual (t , expectedDeposits , resp .Data )
4811+ })
4812+ t .Run ("ssz response" , func (t * testing.T ) {
4813+ req := httptest .NewRequest (http .MethodGet , "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits" , nil )
4814+ req .Header .Set ("Accept" , "application/octet-stream" )
4815+ req .SetPathValue ("state_id" , "head" )
4816+ rec := httptest .NewRecorder ()
4817+ rec .Body = new (bytes.Buffer )
4818+
4819+ server .GetPendingDeposits (rec , req )
4820+ require .Equal (t , http .StatusOK , rec .Code )
4821+ require .Equal (t , "electra" , rec .Header ().Get (api .VersionHeader ))
4822+
4823+ responseBytes := rec .Body .Bytes ()
4824+ var recoveredDeposits []* eth.PendingDeposit
4825+
4826+ // Verify total size matches expected number of deposits
4827+ depositSize := (& eth.PendingDeposit {}).SizeSSZ ()
4828+ require .Equal (t , len (responseBytes ), depositSize * len (deps ))
4829+
4830+ for i := 0 ; i < len (deps ); i ++ {
4831+ start := i * depositSize
4832+ end := start + depositSize
4833+
4834+ var deposit eth.PendingDeposit
4835+ require .NoError (t , deposit .UnmarshalSSZ (responseBytes [start :end ]))
4836+ recoveredDeposits = append (recoveredDeposits , & deposit )
4837+ }
4838+ require .DeepEqual (t , deps , recoveredDeposits )
4839+ })
4840+ t .Run ("pre electra state" , func (t * testing.T ) {
4841+ preElectraSt , _ := util .DeterministicGenesisStateDeneb (t , 1 )
4842+ preElectraServer := & Server {
4843+ Stater : & testutil.MockStater {
4844+ BeaconState : preElectraSt ,
4845+ },
4846+ OptimisticModeFetcher : chainService ,
4847+ FinalizationFetcher : chainService ,
4848+ }
4849+
4850+ // Test JSON request
4851+ req := httptest .NewRequest (http .MethodGet , "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits" , nil )
4852+ req .SetPathValue ("state_id" , "head" )
4853+ rec := httptest .NewRecorder ()
4854+ rec .Body = new (bytes.Buffer )
4855+
4856+ preElectraServer .GetPendingDeposits (rec , req )
4857+ require .Equal (t , http .StatusBadRequest , rec .Code )
4858+
4859+ var errResp struct {
4860+ Code int `json:"code"`
4861+ Message string `json:"message"`
4862+ }
4863+ require .NoError (t , json .Unmarshal (rec .Body .Bytes (), & errResp ))
4864+ require .Equal (t , "state_id is prior to electra" , errResp .Message )
4865+
4866+ // Test SSZ request
4867+ sszReq := httptest .NewRequest (http .MethodGet , "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits" , nil )
4868+ sszReq .Header .Set ("Accept" , "application/octet-stream" )
4869+ sszReq .SetPathValue ("state_id" , "head" )
4870+ sszRec := httptest .NewRecorder ()
4871+ sszRec .Body = new (bytes.Buffer )
4872+
4873+ preElectraServer .GetPendingDeposits (sszRec , sszReq )
4874+ require .Equal (t , http .StatusBadRequest , sszRec .Code )
4875+
4876+ var sszErrResp struct {
4877+ Code int `json:"code"`
4878+ Message string `json:"message"`
4879+ }
4880+ require .NoError (t , json .Unmarshal (sszRec .Body .Bytes (), & sszErrResp ))
4881+ require .Equal (t , "state_id is prior to electra" , sszErrResp .Message )
4882+ })
4883+ t .Run ("missing state_id parameter" , func (t * testing.T ) {
4884+ req := httptest .NewRequest (http .MethodGet , "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits" , nil )
4885+ // Intentionally not setting state_id
4886+ rec := httptest .NewRecorder ()
4887+ rec .Body = new (bytes.Buffer )
4888+
4889+ server .GetPendingDeposits (rec , req )
4890+ require .Equal (t , http .StatusBadRequest , rec .Code )
4891+
4892+ var errResp struct {
4893+ Code int `json:"code"`
4894+ Message string `json:"message"`
4895+ }
4896+ require .NoError (t , json .Unmarshal (rec .Body .Bytes (), & errResp ))
4897+ require .Equal (t , "state_id is required in URL params" , errResp .Message )
4898+ })
4899+ t .Run ("optimistic node" , func (t * testing.T ) {
4900+ optimisticChainService := & chainMock.ChainService {
4901+ Optimistic : true ,
4902+ FinalizedRoots : map [[32 ]byte ]bool {},
4903+ }
4904+ optimisticServer := & Server {
4905+ Stater : server .Stater ,
4906+ OptimisticModeFetcher : optimisticChainService ,
4907+ FinalizationFetcher : optimisticChainService ,
4908+ }
4909+
4910+ req := httptest .NewRequest (http .MethodGet , "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits" , nil )
4911+ req .SetPathValue ("state_id" , "head" )
4912+ rec := httptest .NewRecorder ()
4913+ rec .Body = new (bytes.Buffer )
4914+
4915+ optimisticServer .GetPendingDeposits (rec , req )
4916+ require .Equal (t , http .StatusOK , rec .Code )
4917+
4918+ var resp structs.GetPendingDepositsResponse
4919+ require .NoError (t , json .Unmarshal (rec .Body .Bytes (), & resp ))
4920+ require .Equal (t , true , resp .ExecutionOptimistic )
4921+ })
4922+
4923+ t .Run ("finalized node" , func (t * testing.T ) {
4924+ blockRoot , err := st .LatestBlockHeader ().HashTreeRoot ()
4925+ require .NoError (t , err )
4926+
4927+ finalizedChainService := & chainMock.ChainService {
4928+ Optimistic : false ,
4929+ FinalizedRoots : map [[32 ]byte ]bool {blockRoot : true },
4930+ }
4931+ finalizedServer := & Server {
4932+ Stater : server .Stater ,
4933+ OptimisticModeFetcher : finalizedChainService ,
4934+ FinalizationFetcher : finalizedChainService ,
4935+ }
4936+
4937+ req := httptest .NewRequest (http .MethodGet , "http://example.com/eth/v1/beacon/states/{state_id}/pending_deposits" , nil )
4938+ req .SetPathValue ("state_id" , "head" )
4939+ rec := httptest .NewRecorder ()
4940+ rec .Body = new (bytes.Buffer )
4941+
4942+ finalizedServer .GetPendingDeposits (rec , req )
4943+ require .Equal (t , http .StatusOK , rec .Code )
4944+
4945+ var resp structs.GetPendingDepositsResponse
4946+ require .NoError (t , json .Unmarshal (rec .Body .Bytes (), & resp ))
4947+ require .Equal (t , true , resp .Finalized )
4948+ })
4949+ }
0 commit comments