Skip to content

Commit

Permalink
Merge branch 'main' into getBalance
Browse files Browse the repository at this point in the history
  • Loading branch information
sukantoraymond authored Dec 12, 2024
2 parents e14f5f5 + 76ccb76 commit 9676a70
Show file tree
Hide file tree
Showing 16 changed files with 342 additions and 122 deletions.
18 changes: 1 addition & 17 deletions cmd/blockchaincmd/add_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -681,22 +681,6 @@ func PromptDuration(start time.Time, network models.Network) (time.Duration, err
}
}

func getMaxValidationTime(network models.Network, nodeID ids.NodeID, startTime time.Time) (time.Duration, error) {
ctx, cancel := utils.GetAPIContext()
defer cancel()
platformCli := platformvm.NewClient(network.Endpoint)
vs, err := platformCli.GetCurrentValidators(ctx, avagoconstants.PrimaryNetworkID, nil)
if err != nil {
return 0, err
}
for _, v := range vs {
if v.NodeID == nodeID {
return time.Unix(int64(v.EndTime), 0).Sub(startTime), nil
}
}
return 0, errors.New("nodeID not found in validator set: " + nodeID.String())
}

func getBlockchainTimestamp(network models.Network) (time.Time, error) {
ctx, cancel := utils.GetAPIContext()
defer cancel()
Expand Down Expand Up @@ -782,7 +766,7 @@ func getTimeParameters(network models.Network, nodeID ids.NodeID, isValidator bo
var selectedDuration time.Duration
if useDefaultDuration {
// avoid setting both globals useDefaultDuration and duration
selectedDuration, err = getMaxValidationTime(network, nodeID, start)
selectedDuration, err = utils.GetRemainingValidationTime(network.Endpoint, nodeID, avagoconstants.PrimaryNetworkID, start)
if err != nil {
return time.Time{}, 0, err
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/blockchaincmd/change_weight.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ func setWeight(_ *cobra.Command, args []string) error {
network,
blockchainName,
nodeID,
0, // automatic uptime
isBootstrapValidatorForNetwork(nodeID, sc.Networks[network.Name()]),
false, // don't force
)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions cmd/blockchaincmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -1055,8 +1055,8 @@ func deployBlockchain(cmd *cobra.Command, args []string) error {
aggregatorAllowPrivatePeers,
logLvl,
validatorManagerSDK.PoSParams{
MinimumStakeAmount: utils.ApplyDefaultDenomination(poSMinimumStakeAmount),
MaximumStakeAmount: utils.ApplyDefaultDenomination(poSMaximumStakeAmount),
MinimumStakeAmount: big.NewInt(int64(poSMinimumStakeAmount)),
MaximumStakeAmount: big.NewInt(int64(poSMaximumStakeAmount)),
MinimumStakeDuration: poSMinimumStakeDuration,
MinimumDelegationFee: poSMinimumDelegationFee,
MaximumStakeMultiplier: poSMaximumStakeMultiplier,
Expand Down
80 changes: 66 additions & 14 deletions cmd/blockchaincmd/remove_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import (
"github.com/ava-labs/avalanche-cli/pkg/utils"
"github.com/ava-labs/avalanche-cli/pkg/ux"
"github.com/ava-labs/avalanche-cli/pkg/validatormanager"
validatormanagerSDK "github.com/ava-labs/avalanche-cli/sdk/validatormanager"
"github.com/ava-labs/avalanchego/genesis"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/vms/platformvm/warp"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/spf13/cobra"
)
Expand All @@ -36,6 +38,11 @@ var removeValidatorSupportedNetworkOptions = []networkoptions.NetworkOption{
networkoptions.EtnaDevnet,
}

var (
uptimeSec uint64
force bool
)

// avalanche blockchain removeValidator
func newRemoveValidatorCmd() *cobra.Command {
cmd := &cobra.Command{
Expand All @@ -62,6 +69,8 @@ these prompts by providing the values with flags.`,
privateKeyFlags.AddToCmd(cmd, "to pay fees for completing the validator's removal (blockchain gas token)")
cmd.Flags().StringVar(&rpcURL, "rpc", "", "connect to validator manager at the given rpc endpoint")
cmd.Flags().StringVar(&aggregatorLogLevel, "aggregator-log-level", "Off", "log level to use with signature aggregator")
cmd.Flags().Uint64Var(&uptimeSec, "uptime", 0, "validator's uptime in seconds. If not provided, it will be automatically calculated")
cmd.Flags().BoolVar(&force, "force", false, "force validator removal even if it's not getting rewarded")
return cmd
}

Expand Down Expand Up @@ -172,15 +181,15 @@ func removeValidator(_ *cobra.Command, args []string) error {
}
return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName, nodeID)
}
// check if node is a bootstrap validator to force it to be removed
filteredBootstrapValidators := utils.Filter(scNetwork.BootstrapValidators, func(b models.SubnetValidator) bool {
if id, err := ids.NodeIDFromString(b.NodeID); err == nil && id == nodeID {
return true
}
return false
})
force := len(filteredBootstrapValidators) > 0
if err := removeValidatorSOV(deployer, network, blockchainName, nodeID, force); err != nil {
if err := removeValidatorSOV(
deployer,
network,
blockchainName,
nodeID,
uptimeSec,
isBootstrapValidatorForNetwork(nodeID, scNetwork),
force,
); err != nil {
return err
}
// remove the validator from the list of bootstrap validators
Expand All @@ -199,11 +208,23 @@ func removeValidator(_ *cobra.Command, args []string) error {
return nil
}

func isBootstrapValidatorForNetwork(nodeID ids.NodeID, scNetwork models.NetworkData) bool {
filteredBootstrapValidators := utils.Filter(scNetwork.BootstrapValidators, func(b models.SubnetValidator) bool {
if id, err := ids.NodeIDFromString(b.NodeID); err == nil && id == nodeID {
return true
}
return false
})
return len(filteredBootstrapValidators) > 0
}

func removeValidatorSOV(
deployer *subnet.PublicDeployer,
network models.Network,
blockchainName string,
nodeID ids.NodeID,
uptimeSec uint64,
isBootstrapValidator bool,
force bool,
) error {
chainSpec := contract.ChainSpec{
Expand Down Expand Up @@ -250,7 +271,12 @@ func removeValidatorSOV(
ux.Logger.PrintToUser(logging.Yellow.Wrap("Forcing removal of %s as it is a PoS bootstrap validator"), nodeID)
}

signedMessage, validationID, err := validatormanager.InitValidatorRemoval(
var (
signedMessage *warp.Message
validationID ids.ID
)
// try to remove the validator. If err is "delegator ineligible for rewards" confirm with user and force remove
signedMessage, validationID, err = validatormanager.InitValidatorRemoval(
app,
network,
rpcURL,
Expand All @@ -261,13 +287,40 @@ func removeValidatorSOV(
aggregatorAllowPrivatePeers,
aggregatorLogLevel,
sc.PoS(),
force,
uptimeSec,
isBootstrapValidator || force,
)
if err != nil {
if err != nil && errors.Is(err, validatormanagerSDK.ErrValidatorIneligibleForRewards) {
ux.Logger.PrintToUser("Calculated rewards is zero. Validator %s is not eligible for rewards", nodeID)
force, err = app.Prompt.CaptureNoYes("Do you want to continue with validator removal?")
if err != nil {
return err
}
if !force {
return fmt.Errorf("validator %s is not eligible for rewards. Use --force flag to force removal", nodeID)
}
signedMessage, validationID, err = validatormanager.InitValidatorRemoval(
app,
network,
rpcURL,
chainSpec,
ownerPrivateKey,
nodeID,
extraAggregatorPeers,
aggregatorAllowPrivatePeers,
aggregatorLogLevel,
sc.PoS(),
uptimeSec,
true, // force
)
if err != nil {
return err
}
} else if err != nil {
return err
}
ux.Logger.PrintToUser("ValidationID: %s", validationID)

ux.Logger.PrintToUser("ValidationID: %s", validationID)
txID, _, err := deployer.SetL1ValidatorWeight(signedMessage)
if err != nil {
return err
Expand All @@ -287,7 +340,6 @@ func removeValidatorSOV(
); err != nil {
return err
}

ux.Logger.GreenCheckmarkToUser("Validator successfully removed from the Subnet")

return nil
Expand Down
5 changes: 2 additions & 3 deletions cmd/contractcmd/init_validator_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/ava-labs/avalanche-cli/pkg/models"
"github.com/ava-labs/avalanche-cli/pkg/networkoptions"
"github.com/ava-labs/avalanche-cli/pkg/prompts"
"github.com/ava-labs/avalanche-cli/pkg/utils"
"github.com/ava-labs/avalanche-cli/pkg/ux"
"github.com/ava-labs/avalanche-cli/pkg/validatormanager"
blockchainSDK "github.com/ava-labs/avalanche-cli/sdk/blockchain"
Expand Down Expand Up @@ -206,8 +205,8 @@ func initValidatorManager(_ *cobra.Command, args []string) error {
validatorManagerFlags.aggregatorAllowPrivatePeers,
validatorManagerFlags.aggregatorLogLevel,
validatorManagerSDK.PoSParams{
MinimumStakeAmount: utils.ApplyDefaultDenomination(initPOSManagerFlags.minimumStakeAmount),
MaximumStakeAmount: utils.ApplyDefaultDenomination(initPOSManagerFlags.maximumStakeAmount),
MinimumStakeAmount: big.NewInt(int64(initPOSManagerFlags.minimumStakeAmount)),
MaximumStakeAmount: big.NewInt(int64(initPOSManagerFlags.maximumStakeAmount)),
MinimumStakeDuration: initPOSManagerFlags.minimumStakeDuration,
MinimumDelegationFee: initPOSManagerFlags.minimumDelegationFee,
MaximumStakeMultiplier: initPOSManagerFlags.maximumStakeMultiplier,
Expand Down
2 changes: 2 additions & 0 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,6 @@ const (
MainnetCChainICMRegistryAddress = "0x7C43605E14F391720e1b37E49C78C4b03A488d98"
FujiCChainICMRegistryAddress = "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228"
EtnaDevnetCChainICMRegistryAddress = "0xEe40DFF876204A99eCCB783FDc01eE0a2678Ae93"

ValidatorUptimeDeductible = uint64(10) // seconds to make sure all L1 validators would agree on uptime
)
28 changes: 0 additions & 28 deletions pkg/utils/decimals.go

This file was deleted.

23 changes: 23 additions & 0 deletions pkg/utils/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ package utils
import (
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"net/url"
"regexp"
)

// GetUserIPAddress retrieves the IP address of the user.
Expand Down Expand Up @@ -65,3 +67,24 @@ func IsValidIPPort(ipPortPair string) bool {
}
return true
}

// SplitRPCURI splits the RPC URI into `endpoint` and `chain`.
// Reverse operation of `fmt.Sprintf("%s/ext/bc/%s", endpoint, chain)`.
// returns the `uri` and `chain` as strings, or an error if the request URI is invalid.
func SplitAvalanchegoRPCURI(requestURI string) (string, string, error) {
// Define the regex pattern
pattern := `^(https?://[^/]+)/ext/bc/([^/]+)/rpc$`
regex := regexp.MustCompile(pattern)

// Match the pattern
matches := regex.FindStringSubmatch(requestURI)
if matches == nil || len(matches) != 3 {
return "", "", fmt.Errorf("invalid request URI format")
}

// Extract `endpoint` and `chain`
endpoint := matches[1]
chain := matches[2]

return endpoint, chain, nil
}
76 changes: 69 additions & 7 deletions pkg/utils/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ package utils

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestIsValidIPPort(t *testing.T) {
tests := []struct {
input string
expected bool
}{
{"127.0.0.1:8080", true}, // valid IP:port
{"256.0.0.1:8080", false}, // invalid IP address
{"example.com:8080", false}, // only ip address is allowed
{"127.0.0.1", false}, // missing port
{"[::1]:8080", true}, // valid IPv6 address
{"[::1]", false}, // missing port for IPv6
{"", false}, // empty string
{"127.0.0.1:8080", true}, // valid IP:port
{"256.0.0.1:8080", false}, // invalid IP address
{"127.0.0.1:9650:8080", false}, // only ip address is allowed
{"127.0.0.1", false}, // missing port
{"[::1]:8080", true}, // valid IPv6 address
{"[::1]", false}, // missing port for IPv6
{"", false}, // empty string
}

for _, test := range tests {
Expand All @@ -29,3 +31,63 @@ func TestIsValidIPPort(t *testing.T) {
})
}
}

func TestSplitRPCURI(t *testing.T) {
tests := []struct {
name string
requestURI string
expectedEndpoint string
expectedChain string
expectError bool
}{
{
name: "Valid URI",
requestURI: "http://127.0.0.1:9660/ext/bc/nL95ujcHLPFhuQdHYkvS3CSUvDr9EfZduzyJ5Ty6VXXMgyEEF/rpc",
expectedEndpoint: "http://127.0.0.1:9660",
expectedChain: "nL95ujcHLPFhuQdHYkvS3CSUvDr9EfZduzyJ5Ty6VXXMgyEEF",
expectError: false,
},
{
name: "Valid URI with https",
requestURI: "https://example.com:8080/ext/bc/testChain/rpc",
expectedEndpoint: "https://example.com:8080",
expectedChain: "testChain",
expectError: false,
},
{
name: "Invalid URI - missing /rpc",
requestURI: "http://127.0.0.1:9660/ext/bc/nL95ujcHLPFhuQdHYkvS3CSUvDr9EfZduzyJ5Ty6VXXMgyEEF",
expectedEndpoint: "",
expectedChain: "",
expectError: true,
},
{
name: "Invalid URI - missing /ext/bc/",
requestURI: "http://127.0.0.1:9660/some/other/path/rpc",
expectedEndpoint: "",
expectedChain: "",
expectError: true,
},
{
name: "Invalid URI - malformed URL",
requestURI: "127.0.0.1:9660/ext/bc/chainId/rpc",
expectedEndpoint: "",
expectedChain: "",
expectError: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
endpoint, chain, err := SplitAvalanchegoRPCURI(tt.requestURI)

if tt.expectError {
require.Error(t, err, "expected an error but got nil")
} else {
require.NoError(t, err, "did not expect an error but got one")
require.Equal(t, tt.expectedEndpoint, endpoint, "unexpected Endpoint")
require.Equal(t, tt.expectedChain, chain, "unexpected Chain")
}
})
}
}
Loading

0 comments on commit 9676a70

Please sign in to comment.