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
84 changes: 79 additions & 5 deletions routes/admin_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ type GetGlobalParamsResponse struct {

// The maximum number of copies a single NFT can have.
MaxCopiesPerNFT uint64 `safeForLogging:"true"`

// StakeLockupEpochDuration is the number of epochs that a
// user must wait before unlocking their unstaked stake.
StakeLockupEpochDuration uint64 `safeForLogging:"true"`

// ValidatorJailEpochDuration is the number of epochs that a validator must
// wait after being jailed before submitting an UnjailValidator txn.
ValidatorJailEpochDuration uint64 `safeForLogging:"true"`

// LeaderScheduleMaxNumValidators is the maximum number of validators that
// are included when generating a new Proof-of-Stake leader schedule.
LeaderScheduleMaxNumValidators uint64 `safeForLogging:"true"`

// EpochDurationNumBlocks is the number of blocks included in one epoch.
EpochDurationNumBlocks uint64 `safeForLogging:"true"`

// JailInactiveValidatorGracePeriodEpochs is the number of epochs we
// allow a validator to be inactive for (neither voting nor proposing
// blocks) before they are jailed.
JailInactiveValidatorGracePeriodEpochs uint64 `safeForLogging:"true"`
}

func (fes *APIServer) GetGlobalParams(ww http.ResponseWriter, req *http.Request) {
Expand All @@ -50,11 +70,16 @@ func (fes *APIServer) GetGlobalParams(ww http.ResponseWriter, req *http.Request)
globalParamsEntry := utxoView.GlobalParamsEntry
// Return all the data associated with the transaction in the response
res := GetGlobalParamsResponse{
USDCentsPerBitcoin: globalParamsEntry.USDCentsPerBitcoin,
CreateProfileFeeNanos: globalParamsEntry.CreateProfileFeeNanos,
MinimumNetworkFeeNanosPerKB: globalParamsEntry.MinimumNetworkFeeNanosPerKB,
CreateNFTFeeNanos: globalParamsEntry.CreateNFTFeeNanos,
MaxCopiesPerNFT: globalParamsEntry.MaxCopiesPerNFT,
USDCentsPerBitcoin: globalParamsEntry.USDCentsPerBitcoin,
CreateProfileFeeNanos: globalParamsEntry.CreateProfileFeeNanos,
MinimumNetworkFeeNanosPerKB: globalParamsEntry.MinimumNetworkFeeNanosPerKB,
CreateNFTFeeNanos: globalParamsEntry.CreateNFTFeeNanos,
MaxCopiesPerNFT: globalParamsEntry.MaxCopiesPerNFT,
StakeLockupEpochDuration: utxoView.GetCurrentGlobalParam(lib.StakeLockupEpochDuration),
ValidatorJailEpochDuration: utxoView.GetCurrentGlobalParam(lib.ValidatorJailEpochDuration),
LeaderScheduleMaxNumValidators: utxoView.GetCurrentGlobalParam(lib.LeaderScheduleMaxNumValidators),
EpochDurationNumBlocks: utxoView.GetCurrentGlobalParam(lib.EpochDurationNumBlocks),
JailInactiveValidatorGracePeriodEpochs: utxoView.GetCurrentGlobalParam(lib.JailInactiveValidatorGracePeriodEpochs),
}
if err := json.NewEncoder(ww).Encode(res); err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetGlobalParams: Problem encoding response as JSON: %v", err))
Expand Down Expand Up @@ -84,6 +109,26 @@ type UpdateGlobalParamsRequest struct {
// heights on nonces.
MaxNonceExpirationBlockHeightOffset int64 `safeForLogging:"true"`

// StakeLockupEpochDuration is the number of epochs that a
// user must wait before unlocking their unstaked stake.
StakeLockupEpochDuration uint64 `safeForLogging:"true"`

// ValidatorJailEpochDuration is the number of epochs that a validator must
// wait after being jailed before submitting an UnjailValidator txn.
ValidatorJailEpochDuration uint64 `safeForLogging:"true"`

// LeaderScheduleMaxNumValidators is the maximum number of validators that
// are included when generating a new Proof-of-Stake leader schedule.
LeaderScheduleMaxNumValidators uint64 `safeForLogging:"true"`

// EpochDurationNumBlocks is the number of blocks included in one epoch.
EpochDurationNumBlocks uint64 `safeForLogging:"true"`

// JailInactiveValidatorGracePeriodEpochs is the number of epochs we
// allow a validator to be inactive for (neither voting nor proposing
// blocks) before they are jailed.
JailInactiveValidatorGracePeriodEpochs uint64 `safeForLogging:"true"`

MinFeeRateNanosPerKB uint64 `safeForLogging:"true"`

// No need to specify ProfileEntryResponse in each TransactionFee
Expand Down Expand Up @@ -172,6 +217,34 @@ func (fes *APIServer) UpdateGlobalParams(ww http.ResponseWriter, req *http.Reque
maxNonceExpirationBlockHeightOffset = requestData.MaxNonceExpirationBlockHeightOffset
}

extraData := make(map[string][]byte)

// Update Proof of Stake consensus related global params if they have changed.
if requestData.StakeLockupEpochDuration > 0 &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we typically pass these params as -1 if we don't want to update them in case there's a situation when we'd want them to be 0. I don't think we'll ever want any of these values to be 0 though. Is that assumption correct? It's not a huge deal if we decide to change these later and also provides more safety - if we forget to specify these when making the request, they'll be zero and be left out. okay I've talked myself into it - these are correct.

requestData.StakeLockupEpochDuration != utxoView.GetCurrentGlobalParam(lib.StakeLockupEpochDuration) {
extraData[lib.StakeLockupEpochDuration.ToString()] = lib.UintToBuf(requestData.StakeLockupEpochDuration)
}

if requestData.ValidatorJailEpochDuration > 0 &&
requestData.ValidatorJailEpochDuration != utxoView.GetCurrentGlobalParam(lib.ValidatorJailEpochDuration) {
extraData[lib.ValidatorJailEpochDuration.ToString()] = lib.UintToBuf(requestData.ValidatorJailEpochDuration)
}

if requestData.LeaderScheduleMaxNumValidators > 0 &&
requestData.LeaderScheduleMaxNumValidators != utxoView.GetCurrentGlobalParam(lib.LeaderScheduleMaxNumValidators) {
extraData[lib.LeaderScheduleMaxNumValidators.ToString()] = lib.UintToBuf(requestData.LeaderScheduleMaxNumValidators)
}

if requestData.EpochDurationNumBlocks > 0 &&
requestData.EpochDurationNumBlocks != utxoView.GetCurrentGlobalParam(lib.EpochDurationNumBlocks) {
extraData[lib.EpochDurationNumBlocks.ToString()] = lib.UintToBuf(requestData.EpochDurationNumBlocks)
}

if requestData.JailInactiveValidatorGracePeriodEpochs > 0 &&
requestData.JailInactiveValidatorGracePeriodEpochs != utxoView.GetCurrentGlobalParam(lib.JailInactiveValidatorGracePeriodEpochs) {
extraData[lib.JailInactiveValidatorGracePeriodEpochs.ToString()] = lib.UintToBuf(requestData.JailInactiveValidatorGracePeriodEpochs)
}

// Try and create the update txn for the user.
txn, totalInput, changeAmount, fees, err := fes.blockchain.CreateUpdateGlobalParamsTxn(
updaterPkBytes,
Expand All @@ -182,6 +255,7 @@ func (fes *APIServer) UpdateGlobalParams(ww http.ResponseWriter, req *http.Reque
minimumNetworkFeeNanosPerKb,
[]byte{},
maxNonceExpirationBlockHeightOffset,
extraData,
requestData.MinFeeRateNanosPerKB,
fes.backendServer.GetMempool(), additionalOutputs)
if err != nil {
Expand Down
137 changes: 137 additions & 0 deletions routes/admin_transaction_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package routes

import (
"bytes"
"encoding/json"
"github.com/deso-protocol/core/lib"
"github.com/stretchr/testify/require"
"io"
"net/http"
"net/http/httptest"
"testing"
)

func TestUpdateGlobalParams(t *testing.T) {
// Hard-coded test constants
adminPublicKeyBase58Check := "tBCKWVydPvhXyxSVhntXCw7wUev2fUx64h84FLAfz4JStsdBAq4v9r"
adminJWT := "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ3MDU1Mzh9.LXA2uT8tm-6DXDwTXaCRyqqbFNa96jLl_02LXyAwq58PbVPe28hrICP3P-D5g9mktPJolSVXK_UebRcL5oYCWg"

// Init api server
apiServer := newTestApiServer(t)
apiServer.Config.SuperAdminPublicKeys = []string{adminPublicKeyBase58Check}
senderPkBytes, _, err := lib.Base58CheckDecode(senderPkString)
require.NoError(t, err)
apiServer.Params.ExtraRegtestParamUpdaterKeys[lib.MakePkMapKey(senderPkBytes)] = true

// Helper utils
getGlobalParams := func() *GetGlobalParamsResponse {
// Send POST request.
body := GetGlobalParamsRequest{}
bodyJSON, err := json.Marshal(body)
require.NoError(t, err)
request, _ := http.NewRequest("POST", RoutePathGetGlobalParams, bytes.NewBuffer(bodyJSON))
request.Header.Set("Content-Type", "application/json")
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))
globalParams := GetGlobalParamsResponse{}
err = decoder.Decode(&globalParams)
return &globalParams
}

updateGlobalParams := func(body *UpdateGlobalParamsRequest) {
// Add JWT auth to body of request.
type MergedBody struct {
AdminRequest
UpdateGlobalParamsRequest
}
mergedBody := MergedBody{
AdminRequest: AdminRequest{
JWT: adminJWT, AdminPublicKey: adminPublicKeyBase58Check,
},
UpdateGlobalParamsRequest: *body,
}

// Send POST request.
bodyJSON, err := json.Marshal(mergedBody)
require.NoError(t, err)
request, _ := http.NewRequest("POST", RoutePathUpdateGlobalParams, bytes.NewBuffer(bodyJSON))
request.Header.Set("Content-Type", "application/json")
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))
updateGlobalParamsResponse := UpdateGlobalParamsResponse{}
err = decoder.Decode(&updateGlobalParamsResponse)
require.NoError(t, err)
txn := updateGlobalParamsResponse.Transaction

// Sign txn.
require.Nil(t, txn.Signature.Sign)
signTxn(t, txn, senderPrivString)
require.NotNil(t, txn.Signature.Sign)

// Submit txn.
_, err = submitTxn(t, apiServer, txn)
require.NoError(t, err)
}

// Tests
{
// Confirm default GlobalParams.
globalParams := getGlobalParams()
require.Zero(t, globalParams.MinimumNetworkFeeNanosPerKB)
require.Equal(t, globalParams.StakeLockupEpochDuration, uint64(3))
require.Equal(t, globalParams.ValidatorJailEpochDuration, uint64(3))
require.Equal(t, globalParams.LeaderScheduleMaxNumValidators, uint64(100))
require.Equal(t, globalParams.EpochDurationNumBlocks, uint64(3600))
require.Equal(t, globalParams.JailInactiveValidatorGracePeriodEpochs, uint64(48))
}
{
// Update all GlobalParam fields.
updateGlobalParams(&UpdateGlobalParamsRequest{
UpdaterPublicKeyBase58Check: senderPkString,
MinimumNetworkFeeNanosPerKB: 99,
StakeLockupEpochDuration: 4,
ValidatorJailEpochDuration: 4,
LeaderScheduleMaxNumValidators: 101,
EpochDurationNumBlocks: 3601,
JailInactiveValidatorGracePeriodEpochs: 49,
MinFeeRateNanosPerKB: 99,
})
}
{
// Verify all updated GlobalParam fields.
globalParams := getGlobalParams()
require.Equal(t, globalParams.MinimumNetworkFeeNanosPerKB, uint64(99))
require.Equal(t, globalParams.StakeLockupEpochDuration, uint64(4))
require.Equal(t, globalParams.ValidatorJailEpochDuration, uint64(4))
require.Equal(t, globalParams.LeaderScheduleMaxNumValidators, uint64(101))
require.Equal(t, globalParams.EpochDurationNumBlocks, uint64(3601))
require.Equal(t, globalParams.JailInactiveValidatorGracePeriodEpochs, uint64(49))
}
{
// Update only one GlobalParam field.
updateGlobalParams(&UpdateGlobalParamsRequest{
UpdaterPublicKeyBase58Check: senderPkString,
MinimumNetworkFeeNanosPerKB: 99,
JailInactiveValidatorGracePeriodEpochs: 50,
MinFeeRateNanosPerKB: 99,
})
}
{
// Verify updated GlobalParam field. And other fields retain old values.
globalParams := getGlobalParams()
require.Equal(t, globalParams.MinimumNetworkFeeNanosPerKB, uint64(99))
require.Equal(t, globalParams.StakeLockupEpochDuration, uint64(4))
require.Equal(t, globalParams.ValidatorJailEpochDuration, uint64(4))
require.Equal(t, globalParams.LeaderScheduleMaxNumValidators, uint64(101))
require.Equal(t, globalParams.EpochDurationNumBlocks, uint64(3601))
require.Equal(t, globalParams.JailInactiveValidatorGracePeriodEpochs, uint64(50))
}
}
5 changes: 3 additions & 2 deletions test.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ WORKDIR /deso/src
RUN git clone https://github.com/deso-protocol/core.git

WORKDIR /deso/src/core
RUN git checkout mf/add-bls-signature-utils && \
git pull origin mf/add-bls-signature-utils # TODO: Revert to `git pull` once core PR is merged.
RUN git pull && \
git checkout mf/pos-merge-20230605 && \
git pull origin mf/pos-merge-20230605 # TODO: Revert to `git pull` once core PR is merged.

RUN go mod download
RUN ./scripts/install-relic.sh
Expand Down