Skip to content
Merged
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
7 changes: 7 additions & 0 deletions routes/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,13 @@ func (fes *APIServer) NewRouter() *muxtrace.Router {
fes.UnregisterAsValidator,
PublicAccess,
},
{
"GetValidatorByPublicKeyBase58Check",
[]string{"GET"},
RoutePathValidators + "/{publicKeyBase58Check:t?BC[1-9A-HJ-NP-Za-km-z]{51,53}}",
fes.GetValidatorByPublicKeyBase58Check,
PublicAccess,
},
// Jumio Routes
{
"JumioBegin",
Expand Down
91 changes: 91 additions & 0 deletions routes/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"github.com/deso-protocol/core/bls"
"github.com/deso-protocol/core/lib"
"github.com/gorilla/mux"
"github.com/holiman/uint256"
"io"
"net/http"
)
Expand Down Expand Up @@ -38,6 +40,19 @@ type ValidatorTxnResponse struct {
TxnHashHex string
}

type ValidatorResponse struct {
ValidatorPublicKeyBase58Check string
Domains []string
DisableDelegatedStake bool
VotingPublicKey string
VotingPublicKeySignature string
TotalStakeAmountNanos *uint256.Int
Status string
LastActiveAtEpochNumber uint64
JailedAtEpochNumber uint64
ExtraData map[string]string
}

func (fes *APIServer) RegisterAsValidator(ww http.ResponseWriter, req *http.Request) {
// Decode request body.
decoder := json.NewDecoder(io.LimitReader(req.Body, MaxRequestBodySizeBytes))
Expand Down Expand Up @@ -210,3 +225,79 @@ func (fes *APIServer) UnregisterAsValidator(ww http.ResponseWriter, req *http.Re
return
}
}

func (fes *APIServer) GetValidatorByPublicKeyBase58Check(ww http.ResponseWriter, req *http.Request) {
// Parse ValidatorPublicKeyBase58Check from URL.
vars := mux.Vars(req)
validatorPublicKeyBase58Check, exists := vars["publicKeyBase58Check"]
if !exists {
_AddBadRequestError(ww, "GetValidatorByPublicKeyBase58Check: must provide a ValidatorPublicKeyBase58Check")
return
}

// Create UTXO view.
utxoView, err := fes.backendServer.GetMempool().GetAugmentedUniversalView()
if err != nil {
_AddInternalServerError(ww, "GetValidatorByPublicKeyBase58Check: problem getting UTXO view")
return
}

// Convert ValidatorPublicKeyBase58Check to ValidatorPKID.
validatorPKID, err := fes.getPKIDFromPublicKeyBase58Check(utxoView, validatorPublicKeyBase58Check)
if err != nil || validatorPKID == nil {
_AddInternalServerError(ww, "GetValidatorByPublicKeyBase58Check: problem retrieving validator PKID")
return
}

// Get validator by PKID.
validatorEntry, err := utxoView.GetValidatorByPKID(validatorPKID)
if err != nil {
_AddInternalServerError(ww, "GetValidatorByPublicKeyBase58Check: problem retrieving validator")
return
}
if validatorEntry == nil {
_AddNotFoundError(ww, "GetValidatorByPublicKeyBase58Check: validator not found")
return
}

// Encode response.
validatorResponse := _convertValidatorEntryToResponse(utxoView, validatorEntry, fes.Params)
if err = json.NewEncoder(ww).Encode(validatorResponse); err != nil {
_AddInternalServerError(ww, "GetValidatorByPublicKeyBase58Check: problem encoding response as JSON")
return
}
}

func _convertValidatorEntryToResponse(
utxoView *lib.UtxoView, validatorEntry *lib.ValidatorEntry, params *lib.DeSoParams,
) *ValidatorResponse {
// Nil check: this should never happen but just to be safe.
if validatorEntry == nil {
return &ValidatorResponse{}
}

// Convert ValidatorPKID to ValidatorPublicKeyBase58Check.
validatorPublicKeyBase58Check := lib.Base58CheckEncode(
utxoView.GetPublicKeyForPKID(validatorEntry.ValidatorPKID), false, params,
)

// Convert Domains [][]byte to []string.
var domains []string
for _, domain := range validatorEntry.Domains {
domains = append(domains, string(domain))
}

// Convert ValidatorEntry to ValidatorResponse.
return &ValidatorResponse{
ValidatorPublicKeyBase58Check: validatorPublicKeyBase58Check,
Domains: domains,
DisableDelegatedStake: validatorEntry.DisableDelegatedStake,
VotingPublicKey: validatorEntry.VotingPublicKey.ToString(),
VotingPublicKeySignature: validatorEntry.VotingPublicKeySignature.ToString(),
TotalStakeAmountNanos: validatorEntry.TotalStakeAmountNanos.Clone(),
Status: validatorEntry.Status().ToString(),
LastActiveAtEpochNumber: validatorEntry.LastActiveAtEpochNumber,
JailedAtEpochNumber: validatorEntry.JailedAtEpochNumber,
ExtraData: DecodeExtraDataMap(params, utxoView, validatorEntry.ExtraData),
}
}
59 changes: 51 additions & 8 deletions routes/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@ func TestValidatorRegistration(t *testing.T) {
senderPkBytes, _, err := lib.Base58CheckDecode(senderPkString)
require.NoError(t, err)

// sender creates a VotingPublicKey and VotingSignature.
votingPublicKey, votingSignature := _generateVotingPublicKeyAndSignature(t, senderPkBytes)

{
// sender registers as a validator.

// Send POST request.
votingPublicKey, votingSignature := _generateVotingPublicKeyAndSignature(t, senderPkBytes)
extraData := map[string]string{"Foo": "Bar"}
body := &RegisterAsValidatorRequest{
TransactorPublicKeyBase58Check: senderPkString,
Domains: []string{"https://sender-001.deso.com", "https://sender-002.deso.com"},
DisableDelegatedStake: false,
VotingPublicKey: votingPublicKey.ToString(),
VotingPublicKeySignature: votingSignature.ToString(),
ExtraData: extraData,
ExtraData: map[string]string{"Foo": "Bar"},
MinFeeRateNanosPerKB: apiServer.MinFeeRateNanosPerKB,
TransactionFees: []TransactionFee{},
}
Expand All @@ -51,7 +52,7 @@ func TestValidatorRegistration(t *testing.T) {
err = decoder.Decode(&txnResponse)
require.NoError(t, err)

// Test response fields.
// Verify response fields.
txn := txnResponse.Transaction
require.Equal(t, txn.PublicKey, senderPkBytes)
txnMeta := txn.TxnMeta.(*lib.RegisterAsValidatorMetadata)
Expand All @@ -61,9 +62,8 @@ func TestValidatorRegistration(t *testing.T) {
require.False(t, txnMeta.DisableDelegatedStake)
require.True(t, txnMeta.VotingPublicKey.Eq(votingPublicKey))
require.True(t, txnMeta.VotingPublicKeySignature.Eq(votingSignature))
extraDataEncoded, err := EncodeExtraDataMap(extraData)
require.NoError(t, err)
require.Equal(t, txn.ExtraData, extraDataEncoded)
require.NotNil(t, txn.ExtraData)
require.Equal(t, txn.ExtraData["Foo"], []byte("Bar"))

// Sign txn.
require.Nil(t, txn.Signature.Sign)
Expand All @@ -74,6 +74,36 @@ func TestValidatorRegistration(t *testing.T) {
_, err = submitTxn(t, apiServer, txn)
require.NoError(t, err)
}
{
// get sender validator by PublicKeyBase58Check

// Send GET request.
request, _ := http.NewRequest("GET", RoutePathValidators+"/"+senderPkString, nil)
response := httptest.NewRecorder()
apiServer.router.ServeHTTP(response, request)
require.NotContains(t, string(response.Body.Bytes()), "error")

// Decode response.
decoder := json.NewDecoder(io.LimitReader(response.Body, MaxRequestBodySizeBytes))
validatorResponse := ValidatorResponse{}
err := decoder.Decode(&validatorResponse)
require.NoError(t, err)

// Verify response fields.
require.Equal(t, validatorResponse.ValidatorPublicKeyBase58Check, senderPkString)
require.Len(t, validatorResponse.Domains, 2)
require.Equal(t, validatorResponse.Domains[0], "https://sender-001.deso.com")
require.Equal(t, validatorResponse.Domains[1], "https://sender-002.deso.com")
require.False(t, validatorResponse.DisableDelegatedStake)
require.Equal(t, validatorResponse.VotingPublicKey, votingPublicKey.ToString())
require.Equal(t, validatorResponse.VotingPublicKeySignature, votingSignature.ToString())
require.Equal(t, validatorResponse.TotalStakeAmountNanos.Uint64(), uint64(0))
require.Equal(t, validatorResponse.Status, "Active")
require.Equal(t, validatorResponse.LastActiveAtEpochNumber, uint64(0))
require.Equal(t, validatorResponse.JailedAtEpochNumber, uint64(0))
require.NotNil(t, validatorResponse.ExtraData)
require.Equal(t, validatorResponse.ExtraData["Foo"], "Bar")
}
{
// sender unregisters as a validator.

Expand All @@ -98,7 +128,7 @@ func TestValidatorRegistration(t *testing.T) {
err = decoder.Decode(&txnResponse)
require.NoError(t, err)

// Test response fields.
// Verify response fields.
txn := txnResponse.Transaction
require.Equal(t, txn.PublicKey, senderPkBytes)

Expand All @@ -111,6 +141,19 @@ func TestValidatorRegistration(t *testing.T) {
_, err = submitTxn(t, apiServer, txn)
require.NoError(t, err)
}
{
// get sender validator by PublicKeyBase58Check

// Send GET request.
request, _ := http.NewRequest("GET", RoutePathValidators+"/"+senderPkString, nil)
response := httptest.NewRecorder()
apiServer.router.ServeHTTP(response, request)
responseBody := string(response.Body.Bytes())

// errors: doesn't exist
require.Contains(t, responseBody, "error")
require.Contains(t, responseBody, "validator not found")
}
}

func _generateVotingPublicKeyAndSignature(t *testing.T, transactorPkBytes []byte) (*bls.PublicKey, *bls.Signature) {
Expand Down