@@ -997,17 +997,13 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re
997997 start := time .Now ().UTC ()
998998 registrationTimestampUpperBound := start .Unix () + 10 // 10 seconds from now
999999
1000- numRegTotal := 0
10011000 numRegProcessed := 0
1002- numRegActive := 0
10031001 numRegNew := 0
1004- processingStoppedByError := false
10051002
10061003 // Setup error handling
1007- handleError := func (_log * logrus.Entry , code int , msg string ) {
1008- processingStoppedByError = true
1009- _log .Warnf ("error: %s" , msg )
1010- api .RespondError (w , code , msg )
1004+ logAndReturnError := func (_log * logrus.Entry , code int , userMsg string , err error ) {
1005+ _log .WithError (err ).Warnf ("error: %s" , userMsg )
1006+ api .RespondError (w , code , userMsg )
10111007 }
10121008
10131009 // Start processing
@@ -1036,29 +1032,53 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re
10361032 }
10371033 req .Body .Close ()
10381034
1035+ //
10391036 // Parse the registrations
1040- signedValidatorRegistrations , err := api .fastRegistrationParsing (regBytes , proposerContentType )
1041- if err != nil {
1042- handleError (log , http .StatusBadRequest , err .Error ())
1043- return
1044- }
1037+ //
1038+ signedValidatorRegistrations := new (builderApiV1.SignedValidatorRegistrations )
1039+ if proposerContentType == ApplicationOctetStream {
1040+ // Registrations in SSZ
1041+ log = log .WithField ("is_ssz" , true )
1042+ log .Debug ("Parsing registrations as SSZ" )
10451043
1046- // Iterate over the registrations
1047- for regIndex , signedValidatorRegistration := range signedValidatorRegistrations .Registrations {
1048- numRegTotal += 1
1049- if processingStoppedByError {
1044+ timeStart := time . Now ()
1045+ err := signedValidatorRegistrations .UnmarshalSSZ ( regBytes )
1046+ if err != nil {
1047+ logAndReturnError ( log , http . StatusBadRequest , err . Error (), err )
10501048 return
10511049 }
1050+ log .WithFields (logrus.Fields {
1051+ "sszDecodeDurationMs" : time .Since (timeStart ).Milliseconds (),
1052+ "numRegistrations" : len (signedValidatorRegistrations .Registrations ),
1053+ }).Debug ("Parsed registrations as SSZ" )
1054+ } else {
1055+ // Registrations in JSON
1056+ log = log .WithField ("is_ssz" , false )
1057+ api .log .Debug ("Parsing registrations as JSON" )
1058+
1059+ timeStart := time .Now ()
1060+ signedValidatorRegistrations , err = api .parseValidatorRegistrationsJSON (regBytes )
1061+ if err != nil {
1062+ logAndReturnError (log , http .StatusBadRequest , err .Error (), err )
1063+ return
1064+ }
1065+ log .WithFields (logrus.Fields {
1066+ "jsonDecodeDurationMs" : time .Since (timeStart ).Milliseconds (),
1067+ "numRegistrations" : len (signedValidatorRegistrations .Registrations ),
1068+ }).Debug ("Parsed registrations as JSON" )
1069+ }
1070+
1071+ //
1072+ // Iterate over the registrations and process them
1073+ //
1074+ for regIndex , signedValidatorRegistration := range signedValidatorRegistrations .Registrations {
10521075 numRegProcessed += 1
1076+ pkHex := common .NewPubkeyHex (signedValidatorRegistration .Message .Pubkey .String ())
1077+
10531078 regLog := log .WithFields (logrus.Fields {
10541079 "regIndex" : regIndex ,
1055- "numRegistrationsSoFar" : numRegTotal ,
10561080 "numRegistrationsProcessed" : numRegProcessed ,
1057- })
10581081
1059- // Add validator pubkey to logs
1060- pkHex := common .PubkeyHex (signedValidatorRegistration .Message .Pubkey .String ())
1061- regLog = regLog .WithFields (logrus.Fields {
10621082 "pubkey" : pkHex ,
10631083 "signature" : signedValidatorRegistration .Signature .String (),
10641084 "feeRecipient" : signedValidatorRegistration .Message .FeeRecipient .String (),
@@ -1069,45 +1089,60 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re
10691089 // Ensure a valid timestamp (not too early, and not too far in the future)
10701090 registrationTimestamp := signedValidatorRegistration .Message .Timestamp .Unix ()
10711091 if registrationTimestamp < int64 (api .genesisInfo .Data .GenesisTime ) { //nolint:gosec
1072- handleError (regLog , http .StatusBadRequest , "timestamp too early" )
1073- return
1092+ logAndReturnError (regLog , http .StatusBadRequest , "timestamp too early" , nil )
1093+ break
10741094 } else if registrationTimestamp > registrationTimestampUpperBound {
1075- handleError (regLog , http .StatusBadRequest , "timestamp too far in the future" )
1076- return
1095+ logAndReturnError (regLog , http .StatusBadRequest , "timestamp too far in the future" , nil )
1096+ break
10771097 }
10781098
10791099 // Check if a real validator
10801100 isKnownValidator := api .datastore .IsKnownValidator (pkHex )
10811101 if ! isKnownValidator {
1082- handleError (regLog , http .StatusBadRequest , fmt .Sprintf ("not a known validator: %s" , pkHex ))
1083- return
1102+ logAndReturnError (regLog , http .StatusBadRequest , fmt .Sprintf ("not a known validator: %s" , pkHex ), nil )
1103+ break
10841104 }
10851105
1086- // Check for a previous registration timestamp
1087- prevTimestamp , err := api .redis .GetValidatorRegistrationTimestamp (pkHex )
1106+ // Check for a previous registration timestamp and see if fields changed
1107+ cachedRegistrationData , err := api .redis .GetValidatorRegistrationData (pkHex )
1108+ haveCachedRegistration := cachedRegistrationData != nil
1109+
10881110 if err != nil {
1089- regLog .WithError (err ).Error ("error getting last registration timestamp" )
1090- } else if prevTimestamp >= uint64 (signedValidatorRegistration .Message .Timestamp .Unix ()) { //nolint:gosec
1091- // abort if the current registration timestamp is older or equal to the last known one
1092- return
1111+ regLog .WithError (err ).Error ("error getting last registration" ) // maybe a Redis error. continue to validation + processing
1112+ } else if haveCachedRegistration {
1113+ // See if we can discard (if no fields changed, or old timestamp)
1114+ isChangedFeeRecipient := cachedRegistrationData .FeeRecipient != signedValidatorRegistration .Message .FeeRecipient
1115+ isChangedGasLimit := cachedRegistrationData .GasLimit != signedValidatorRegistration .Message .GasLimit
1116+ isNewerTimestamp := signedValidatorRegistration .Message .Timestamp .UTC ().Unix () > cachedRegistrationData .Timestamp .UTC ().Unix ()
1117+
1118+ // If key fields haven't changed, can just discard without signature validation
1119+ if ! isChangedFeeRecipient && ! isChangedGasLimit {
1120+ continue
1121+ }
1122+
1123+ // Ensure it's not a replay of an old registration
1124+ if ! isNewerTimestamp {
1125+ continue
1126+ }
10931127 }
10941128
10951129 // Verify the signature
1130+ regLog .Debug ("verifying BLS signature..." )
10961131 ok , err := ssz .VerifySignature (signedValidatorRegistration .Message , api .opts .EthNetDetails .DomainBuilder , signedValidatorRegistration .Message .Pubkey [:], signedValidatorRegistration .Signature [:])
10971132 if err != nil {
10981133 regLog .WithError (err ).Error ("error verifying registerValidator signature" )
1099- return
1134+ break
11001135 } else if ! ok {
11011136 regLog .Info ("invalid validator signature" )
11021137 if api .ffRegValContinueOnInvalidSig {
1103- return
1138+ continue
11041139 } else {
1105- handleError (regLog , http .StatusBadRequest , "failed to verify validator signature for " + signedValidatorRegistration .Message .Pubkey .String ())
1106- return
1140+ logAndReturnError (regLog , http .StatusBadRequest , "failed to verify validator signature for " + signedValidatorRegistration .Message .Pubkey .String (), err )
1141+ break
11071142 }
11081143 }
11091144
1110- // Now we have a new registration to process
1145+ // Now we have a new registration to process (store in DB + Cache)
11111146 numRegNew += 1
11121147
11131148 // Save to database
@@ -1121,18 +1156,11 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re
11211156 log = log .WithFields (logrus.Fields {
11221157 "timeNeededSec" : time .Since (start ).Seconds (),
11231158 "timeNeededMs" : time .Since (start ).Milliseconds (),
1124- "numRegistrations" : numRegTotal ,
1125- "numRegistrationsActive" : numRegActive ,
1159+ "numRegistrations" : len (signedValidatorRegistrations .Registrations ),
11261160 "numRegistrationsProcessed" : numRegProcessed ,
11271161 "numRegistrationsNew" : numRegNew ,
1128- "processingStoppedByError" : processingStoppedByError ,
11291162 })
11301163
1131- if err != nil {
1132- handleError (log , http .StatusBadRequest , "error in traversing json" )
1133- return
1134- }
1135-
11361164 // notify that new registrations are available
11371165 select {
11381166 case api .validatorUpdateCh <- struct {}{}:
@@ -1143,19 +1171,9 @@ func (api *RelayAPI) handleRegisterValidator(w http.ResponseWriter, req *http.Re
11431171 w .WriteHeader (http .StatusOK )
11441172}
11451173
1146- func (api * RelayAPI ) fastRegistrationParsing (regBytes []byte , contentType string ) (* builderApiV1.SignedValidatorRegistrations , error ) {
1174+ func (api * RelayAPI ) parseValidatorRegistrationsJSON (regBytes []byte ) (* builderApiV1.SignedValidatorRegistrations , error ) {
11471175 signedValidatorRegistrations := new (builderApiV1.SignedValidatorRegistrations )
11481176
1149- // Parse registrations as SSZ
1150- if contentType == ApplicationOctetStream {
1151- api .log .Debug ("Parsing registrations as SSZ" )
1152- err := signedValidatorRegistrations .UnmarshalSSZ (regBytes )
1153- if err != nil {
1154- return nil , err
1155- }
1156- return signedValidatorRegistrations , nil
1157- }
1158-
11591177 // Parse registrations as JSON
11601178 parseRegistration := func (value []byte ) (reg * builderApiV1.SignedValidatorRegistration , err error ) {
11611179 // Pubkey
@@ -1231,7 +1249,6 @@ func (api *RelayAPI) fastRegistrationParsing(regBytes []byte, contentType string
12311249 }
12321250
12331251 var parseErr error
1234- api .log .Debug ("Parsing registrations as JSON" )
12351252 _ , forEachErr := jsonparser .ArrayEach (regBytes , func (value []byte , dataType jsonparser.ValueType , offset int , err error ) {
12361253 if err != nil {
12371254 parseErr = err
0 commit comments