add permissionless validators

This commit is contained in:
sukantoraymond
2023-05-22 17:56:39 -04:00
parent a8cd1e3f1b
commit 149216b09c
10 changed files with 1475 additions and 779 deletions
+10
View File
@@ -34,6 +34,7 @@ type Client interface {
CreateBlockchains(ctx context.Context, blockchainSpecs []*rpcpb.BlockchainSpec) (*rpcpb.CreateBlockchainsResponse, error)
CreateSubnets(ctx context.Context, subnetSpecs []*rpcpb.SubnetSpec) (*rpcpb.CreateSubnetsResponse, error)
TransformElasticSubnets(ctx context.Context, elasticSubnetSpecs []*rpcpb.ElasticSubnetSpec) (*rpcpb.TransformElasticSubnetsResponse, error)
AddPermissionlessValidator(ctx context.Context, validatorSpec []*rpcpb.PermissionlessValidatorSpec) (*rpcpb.AddPermissionlessValidatorResponse, error)
Health(ctx context.Context) (*rpcpb.HealthResponse, error)
WaitForHealthy(ctx context.Context) (*rpcpb.WaitForHealthyResponse, error)
URIs(ctx context.Context) ([]string, error)
@@ -168,6 +169,15 @@ func (c *client) TransformElasticSubnets(ctx context.Context, elasticSubnetSpecs
return c.controlc.TransformElasticSubnets(ctx, req)
}
func (c *client) AddPermissionlessValidator(ctx context.Context, validatorSpec []*rpcpb.PermissionlessValidatorSpec) (*rpcpb.AddPermissionlessValidatorResponse, error) {
req := &rpcpb.AddPermissionlessValidatorRequest{
ValidatorSpec: validatorSpec,
}
c.log.Info("add permissionless validators to elastic subnets")
return c.controlc.AddPermissionlessValidator(ctx, req)
}
func (c *client) Health(ctx context.Context) (*rpcpb.HealthResponse, error) {
c.log.Info("health")
return c.controlc.Health(ctx, &rpcpb.HealthRequest{})
+38
View File
@@ -61,6 +61,7 @@ func NewCommand() *cobra.Command {
newCreateBlockchainsCommand(),
newCreateSubnetsCommand(),
newTransformElasticSubnetsCommand(),
newAddPermissionlessValidatorCommand(),
newHealthCommand(),
newWaitForHealthyCommand(),
newURIsCommand(),
@@ -412,6 +413,15 @@ func newTransformElasticSubnetsCommand() *cobra.Command {
return cmd
}
func newAddPermissionlessValidatorCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "add-permissionless-validator [options]",
Short: "Add permissionelss validator to elastic subnets.",
RunE: addPermissionlessValidatorFunc,
Args: cobra.ExactArgs(1),
}
return cmd
}
func transformElasticSubnetsFunc(_ *cobra.Command, args []string) error {
cli, err := newClient()
if err != nil {
@@ -440,6 +450,34 @@ func transformElasticSubnetsFunc(_ *cobra.Command, args []string) error {
return nil
}
func addPermissionlessValidatorFunc(_ *cobra.Command, args []string) error {
cli, err := newClient()
if err != nil {
return err
}
defer cli.Close()
validatorSpecStr := args[0]
validatorSpec := []*rpcpb.PermissionlessValidatorSpec{}
if err := json.Unmarshal([]byte(validatorSpecStr), &validatorSpec); err != nil {
return err
}
ctx := getAsyncContext()
info, err := cli.AddPermissionlessValidator(
ctx,
validatorSpec,
)
if err != nil {
return err
}
ux.Print(log, logging.Green.Wrap("add-permissionless-validator response: %+v"), info)
return nil
}
func newHealthCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "health [options]",
+88
View File
@@ -8,6 +8,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/ava-labs/avalanchego/vms/platformvm/reward"
"os"
"path/filepath"
"sort"
@@ -53,6 +54,8 @@ const (
// check period while waiting for all validators to be ready
waitForValidatorsPullFrequency = time.Second
defaultTimeout = time.Minute
stakingMinimumLeadTime = 25 * time.Second
minStakeDuration = 24 * 14 * time.Hour
)
var (
@@ -150,6 +153,16 @@ func (ln *localNetwork) RegisterBlockchainAliases(
return nil
}
func (ln *localNetwork) AddPermissionlessValidator(
ctx context.Context,
validatorSpec []network.PermissionlessValidatorSpec,
) ([]ids.ID, error) {
ln.lock.Lock()
defer ln.lock.Unlock()
return ln.addPermissionlessValidators(ctx, validatorSpec)
}
func (ln *localNetwork) TransformSubnet(
ctx context.Context,
elasticSubnetConfig []network.ElasticSubnetSpec,
@@ -769,6 +782,81 @@ func importPChainFromXChain(ctx context.Context, w *wallet, owner *secp256k1fx.O
return err
}
func (ln *localNetwork) addPermissionlessValidators(
ctx context.Context,
validatorSpecs []network.PermissionlessValidatorSpec,
) ([]ids.ID, error) {
ln.log.Info("adding permissionless validator tx")
validatorSpecIDs := make([]ids.ID, len(validatorSpecs))
clientURI, err := ln.getClientURI()
if err != nil {
return nil, err
}
// wallet needs txs for all previously created subnets
var preloadTXs []ids.ID
for _, validatorSpec := range validatorSpecs {
if validatorSpec.SubnetID == nil {
return nil, errors.New("permissionless validator spec has no subnet ID")
} else {
subnetID, err := ids.FromString(*validatorSpec.SubnetID)
if err != nil {
return nil, err
}
preloadTXs = append(preloadTXs, subnetID)
}
}
w, err := newWallet(ctx, clientURI, preloadTXs)
if err != nil {
return nil, err
}
owner := &secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
w.addr,
},
}
for i, validatorSpec := range validatorSpecs {
ln.log.Info(logging.Green.Wrap("adding permissionless validator"), zap.String("node ID", validatorSpec.NodeID))
cctx, cancel := createDefaultCtx(ctx)
validatoreNodeID, err := ids.NodeIDFromString(validatorSpec.NodeID)
if err != nil {
return nil, err
}
subnetID, err := ids.FromString(*validatorSpec.SubnetID)
if err != nil {
return nil, err
}
assetID, err := ids.FromString(validatorSpec.AssetID)
if err != nil {
return nil, err
}
txID, err := w.pWallet.IssueAddPermissionlessValidatorTx(
&txs.SubnetValidator{
Validator: txs.Validator{
NodeID: validatoreNodeID,
Start: uint64(validatorSpec.StartTime.Unix()),
End: uint64(validatorSpec.StartTime.Add(validatorSpec.StakeDuration).Unix()),
Wght: validatorSpec.StakedAmount,
},
Subnet: subnetID,
},
&signer.Empty{},
assetID,
owner,
&secp256k1fx.OutputOwners{},
reward.PercentDenominator,
common.WithContext(cctx),
defaultPoll,
)
cancel()
if err != nil {
return nil, err
}
ln.log.Info("Validator successfully added as permissionless validator", zap.String("TX ID", txID.String()))
validatorSpecIDs[i] = txID
}
return validatorSpecIDs, nil
}
func (ln *localNetwork) transformToElasticSubnets(
ctx context.Context,
elasticSubnetSpecs []network.ElasticSubnetSpec,
+11
View File
@@ -15,6 +15,15 @@ var (
ErrNodeNotFound = errors.New("node not found in network")
)
type PermissionlessValidatorSpec struct {
SubnetID *string
AssetID string
NodeID string
StakedAmount uint64
StartTime time.Time
StakeDuration time.Duration
}
type ElasticSubnetSpec struct {
SubnetID *string
AssetName string
@@ -98,4 +107,6 @@ type Network interface {
CreateSubnets(context.Context, []SubnetSpec) ([]ids.ID, error)
// Transform subnet into elastic subnet
TransformSubnet(context.Context, []ElasticSubnetSpec) ([]ids.ID, error)
// Add a validator into an elastic subnet
AddPermissionlessValidator(context.Context, []PermissionlessValidatorSpec) ([]ids.ID, error)
}
+1024 -753
View File
File diff suppressed because it is too large Load Diff
+88 -3
View File
@@ -201,6 +201,40 @@ func local_request_ControlService_TransformElasticSubnets_0(ctx context.Context,
}
func request_ControlService_AddPermissionlessValidator_0(ctx context.Context, marshaler runtime.Marshaler, client ControlServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AddPermissionlessValidatorRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.AddPermissionlessValidator(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_ControlService_AddPermissionlessValidator_0(ctx context.Context, marshaler runtime.Marshaler, server ControlServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AddPermissionlessValidatorRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AddPermissionlessValidator(ctx, &protoReq)
return msg, metadata, err
}
func request_ControlService_CreateSubnets_0(ctx context.Context, marshaler runtime.Marshaler, client ControlServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CreateSubnetsRequest
var metadata runtime.ServerMetadata
@@ -927,7 +961,7 @@ func RegisterControlServiceHandlerServer(ctx context.Context, mux *runtime.Serve
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/rpcpb.ControlService/TransformElasticSubnets", runtime.WithHTTPPathPattern("/v1/control/transformelasticsubnet"))
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/rpcpb.ControlService/TransformElasticSubnets", runtime.WithHTTPPathPattern("/v1/control/transformelasticsubnets"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@@ -944,6 +978,31 @@ func RegisterControlServiceHandlerServer(ctx context.Context, mux *runtime.Serve
})
mux.Handle("POST", pattern_ControlService_AddPermissionlessValidator_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/rpcpb.ControlService/AddPermissionlessValidator", runtime.WithHTTPPathPattern("/v1/control/addpermissionlessvalidator"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_ControlService_AddPermissionlessValidator_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_ControlService_AddPermissionlessValidator_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ControlService_CreateSubnets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1560,7 +1619,7 @@ func RegisterControlServiceHandlerClient(ctx context.Context, mux *runtime.Serve
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/rpcpb.ControlService/TransformElasticSubnets", runtime.WithHTTPPathPattern("/v1/control/transformelasticsubnet"))
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/rpcpb.ControlService/TransformElasticSubnets", runtime.WithHTTPPathPattern("/v1/control/transformelasticsubnets"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@@ -1576,6 +1635,28 @@ func RegisterControlServiceHandlerClient(ctx context.Context, mux *runtime.Serve
})
mux.Handle("POST", pattern_ControlService_AddPermissionlessValidator_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/rpcpb.ControlService/AddPermissionlessValidator", runtime.WithHTTPPathPattern("/v1/control/addpermissionlessvalidator"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ControlService_AddPermissionlessValidator_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_ControlService_AddPermissionlessValidator_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ControlService_CreateSubnets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1982,7 +2063,9 @@ var (
pattern_ControlService_CreateBlockchains_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "createblockchains"}, ""))
pattern_ControlService_TransformElasticSubnets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "transformelasticsubnet"}, ""))
pattern_ControlService_TransformElasticSubnets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "transformelasticsubnets"}, ""))
pattern_ControlService_AddPermissionlessValidator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "addpermissionlessvalidator"}, ""))
pattern_ControlService_CreateSubnets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "createsubnets"}, ""))
@@ -2030,6 +2113,8 @@ var (
forward_ControlService_TransformElasticSubnets_0 = runtime.ForwardResponseMessage
forward_ControlService_AddPermissionlessValidator_0 = runtime.ForwardResponseMessage
forward_ControlService_CreateSubnets_0 = runtime.ForwardResponseMessage
forward_ControlService_Health_0 = runtime.ForwardResponseMessage
+25
View File
@@ -50,6 +50,13 @@ service ControlService {
};
}
rpc AddPermissionlessValidator(AddPermissionlessValidatorRequest) returns (AddPermissionlessValidatorResponse) {
option (google.api.http) = {
post: "/v1/control/addpermissionlessvalidator"
body: "*"
};
}
rpc CreateSubnets(CreateSubnetsRequest) returns (CreateSubnetsResponse) {
option (google.api.http) = {
post: "/v1/control/createsubnets"
@@ -344,6 +351,24 @@ message TransformElasticSubnetsResponse {
repeated string tx_ids = 2;
}
message PermissionlessValidatorSpec {
string subnet_id = 1;
string node_id = 2;
uint64 staked_token_amount = 3;
string asset_id = 4;
string start_time = 5;
uint64 stake_duration = 6;
}
message AddPermissionlessValidatorRequest {
repeated PermissionlessValidatorSpec validator_spec = 1;
}
message AddPermissionlessValidatorResponse {
ClusterInfo cluster_info = 1;
repeated string tx_ids = 2;
}
message BlockchainSpec {
string vm_name = 1;
// either file path or file contents
+59 -22
View File
@@ -109,28 +109,29 @@ var PingService_ServiceDesc = grpc.ServiceDesc{
}
const (
ControlService_RPCVersion_FullMethodName = "/rpcpb.ControlService/RPCVersion"
ControlService_Start_FullMethodName = "/rpcpb.ControlService/Start"
ControlService_CreateBlockchains_FullMethodName = "/rpcpb.ControlService/CreateBlockchains"
ControlService_TransformElasticSubnets_FullMethodName = "/rpcpb.ControlService/TransformElasticSubnets"
ControlService_CreateSubnets_FullMethodName = "/rpcpb.ControlService/CreateSubnets"
ControlService_Health_FullMethodName = "/rpcpb.ControlService/Health"
ControlService_URIs_FullMethodName = "/rpcpb.ControlService/URIs"
ControlService_WaitForHealthy_FullMethodName = "/rpcpb.ControlService/WaitForHealthy"
ControlService_Status_FullMethodName = "/rpcpb.ControlService/Status"
ControlService_StreamStatus_FullMethodName = "/rpcpb.ControlService/StreamStatus"
ControlService_RemoveNode_FullMethodName = "/rpcpb.ControlService/RemoveNode"
ControlService_AddNode_FullMethodName = "/rpcpb.ControlService/AddNode"
ControlService_RestartNode_FullMethodName = "/rpcpb.ControlService/RestartNode"
ControlService_PauseNode_FullMethodName = "/rpcpb.ControlService/PauseNode"
ControlService_ResumeNode_FullMethodName = "/rpcpb.ControlService/ResumeNode"
ControlService_Stop_FullMethodName = "/rpcpb.ControlService/Stop"
ControlService_AttachPeer_FullMethodName = "/rpcpb.ControlService/AttachPeer"
ControlService_SendOutboundMessage_FullMethodName = "/rpcpb.ControlService/SendOutboundMessage"
ControlService_SaveSnapshot_FullMethodName = "/rpcpb.ControlService/SaveSnapshot"
ControlService_LoadSnapshot_FullMethodName = "/rpcpb.ControlService/LoadSnapshot"
ControlService_RemoveSnapshot_FullMethodName = "/rpcpb.ControlService/RemoveSnapshot"
ControlService_GetSnapshotNames_FullMethodName = "/rpcpb.ControlService/GetSnapshotNames"
ControlService_RPCVersion_FullMethodName = "/rpcpb.ControlService/RPCVersion"
ControlService_Start_FullMethodName = "/rpcpb.ControlService/Start"
ControlService_CreateBlockchains_FullMethodName = "/rpcpb.ControlService/CreateBlockchains"
ControlService_TransformElasticSubnets_FullMethodName = "/rpcpb.ControlService/TransformElasticSubnets"
ControlService_AddPermissionlessValidator_FullMethodName = "/rpcpb.ControlService/AddPermissionlessValidator"
ControlService_CreateSubnets_FullMethodName = "/rpcpb.ControlService/CreateSubnets"
ControlService_Health_FullMethodName = "/rpcpb.ControlService/Health"
ControlService_URIs_FullMethodName = "/rpcpb.ControlService/URIs"
ControlService_WaitForHealthy_FullMethodName = "/rpcpb.ControlService/WaitForHealthy"
ControlService_Status_FullMethodName = "/rpcpb.ControlService/Status"
ControlService_StreamStatus_FullMethodName = "/rpcpb.ControlService/StreamStatus"
ControlService_RemoveNode_FullMethodName = "/rpcpb.ControlService/RemoveNode"
ControlService_AddNode_FullMethodName = "/rpcpb.ControlService/AddNode"
ControlService_RestartNode_FullMethodName = "/rpcpb.ControlService/RestartNode"
ControlService_PauseNode_FullMethodName = "/rpcpb.ControlService/PauseNode"
ControlService_ResumeNode_FullMethodName = "/rpcpb.ControlService/ResumeNode"
ControlService_Stop_FullMethodName = "/rpcpb.ControlService/Stop"
ControlService_AttachPeer_FullMethodName = "/rpcpb.ControlService/AttachPeer"
ControlService_SendOutboundMessage_FullMethodName = "/rpcpb.ControlService/SendOutboundMessage"
ControlService_SaveSnapshot_FullMethodName = "/rpcpb.ControlService/SaveSnapshot"
ControlService_LoadSnapshot_FullMethodName = "/rpcpb.ControlService/LoadSnapshot"
ControlService_RemoveSnapshot_FullMethodName = "/rpcpb.ControlService/RemoveSnapshot"
ControlService_GetSnapshotNames_FullMethodName = "/rpcpb.ControlService/GetSnapshotNames"
)
// ControlServiceClient is the client API for ControlService service.
@@ -141,6 +142,7 @@ type ControlServiceClient interface {
Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error)
CreateBlockchains(ctx context.Context, in *CreateBlockchainsRequest, opts ...grpc.CallOption) (*CreateBlockchainsResponse, error)
TransformElasticSubnets(ctx context.Context, in *TransformElasticSubnetsRequest, opts ...grpc.CallOption) (*TransformElasticSubnetsResponse, error)
AddPermissionlessValidator(ctx context.Context, in *AddPermissionlessValidatorRequest, opts ...grpc.CallOption) (*AddPermissionlessValidatorResponse, error)
CreateSubnets(ctx context.Context, in *CreateSubnetsRequest, opts ...grpc.CallOption) (*CreateSubnetsResponse, error)
Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error)
URIs(ctx context.Context, in *URIsRequest, opts ...grpc.CallOption) (*URIsResponse, error)
@@ -205,6 +207,15 @@ func (c *controlServiceClient) TransformElasticSubnets(ctx context.Context, in *
return out, nil
}
func (c *controlServiceClient) AddPermissionlessValidator(ctx context.Context, in *AddPermissionlessValidatorRequest, opts ...grpc.CallOption) (*AddPermissionlessValidatorResponse, error) {
out := new(AddPermissionlessValidatorResponse)
err := c.cc.Invoke(ctx, ControlService_AddPermissionlessValidator_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlServiceClient) CreateSubnets(ctx context.Context, in *CreateSubnetsRequest, opts ...grpc.CallOption) (*CreateSubnetsResponse, error) {
out := new(CreateSubnetsResponse)
err := c.cc.Invoke(ctx, ControlService_CreateSubnets_FullMethodName, in, out, opts...)
@@ -398,6 +409,7 @@ type ControlServiceServer interface {
Start(context.Context, *StartRequest) (*StartResponse, error)
CreateBlockchains(context.Context, *CreateBlockchainsRequest) (*CreateBlockchainsResponse, error)
TransformElasticSubnets(context.Context, *TransformElasticSubnetsRequest) (*TransformElasticSubnetsResponse, error)
AddPermissionlessValidator(context.Context, *AddPermissionlessValidatorRequest) (*AddPermissionlessValidatorResponse, error)
CreateSubnets(context.Context, *CreateSubnetsRequest) (*CreateSubnetsResponse, error)
Health(context.Context, *HealthRequest) (*HealthResponse, error)
URIs(context.Context, *URIsRequest) (*URIsResponse, error)
@@ -435,6 +447,9 @@ func (UnimplementedControlServiceServer) CreateBlockchains(context.Context, *Cre
func (UnimplementedControlServiceServer) TransformElasticSubnets(context.Context, *TransformElasticSubnetsRequest) (*TransformElasticSubnetsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method TransformElasticSubnets not implemented")
}
func (UnimplementedControlServiceServer) AddPermissionlessValidator(context.Context, *AddPermissionlessValidatorRequest) (*AddPermissionlessValidatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddPermissionlessValidator not implemented")
}
func (UnimplementedControlServiceServer) CreateSubnets(context.Context, *CreateSubnetsRequest) (*CreateSubnetsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateSubnets not implemented")
}
@@ -574,6 +589,24 @@ func _ControlService_TransformElasticSubnets_Handler(srv interface{}, ctx contex
return interceptor(ctx, in, info, handler)
}
func _ControlService_AddPermissionlessValidator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddPermissionlessValidatorRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServiceServer).AddPermissionlessValidator(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ControlService_AddPermissionlessValidator_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServiceServer).AddPermissionlessValidator(ctx, req.(*AddPermissionlessValidatorRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ControlService_CreateSubnets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateSubnetsRequest)
if err := dec(in); err != nil {
@@ -924,6 +957,10 @@ var ControlService_ServiceDesc = grpc.ServiceDesc{
MethodName: "TransformElasticSubnets",
Handler: _ControlService_TransformElasticSubnets_Handler,
},
{
MethodName: "AddPermissionlessValidator",
Handler: _ControlService_AddPermissionlessValidator_Handler,
},
{
MethodName: "CreateSubnets",
Handler: _ControlService_CreateSubnets_Handler,
+39
View File
@@ -293,6 +293,45 @@ func (lc *localNetwork) CreateChains(
return chainIDs, nil
}
func (lc *localNetwork) AddPermissionlessValidator(ctx context.Context, validatorSpecs []network.PermissionlessValidatorSpec) ([]ids.ID, error) {
lc.lock.Lock()
defer lc.lock.Unlock()
if len(validatorSpecs) == 0 {
ux.Print(lc.log, logging.Orange.Wrap(logging.Bold.Wrap("no validator specs provided...")))
return nil, nil
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
go func(ctx context.Context) {
select {
case <-lc.stopCh:
// The network is stopped; return from method calls below.
cancel()
case <-ctx.Done():
// This method is done. Don't leak [ctx].
}
}(ctx)
if err := lc.awaitHealthyAndUpdateNetworkInfo(ctx); err != nil {
return nil, err
}
txIDs, err := lc.nw.AddPermissionlessValidator(ctx, validatorSpecs)
if err != nil {
return nil, err
}
if err := lc.awaitHealthyAndUpdateNetworkInfo(ctx); err != nil {
return nil, err
}
ux.Print(lc.log, logging.Green.Wrap(logging.Bold.Wrap("finished adding permissionless validators")))
return txIDs, nil
}
func (lc *localNetwork) TransformSubnets(ctx context.Context, elasticSubnetSpecs []network.ElasticSubnetSpec) ([]ids.ID, error) {
lc.lock.Lock()
defer lc.lock.Unlock()
+93 -1
View File
@@ -51,7 +51,9 @@ const (
defaultStartTimeout = 5 * time.Minute
waitForHealthyTimeout = 3 * time.Minute
networkRootDirPrefix = "network"
networkRootDirPrefix = "network"
timeParseLayout = "2006-01-02 15:04:05"
stakingMinimumLeadTime = 25 * time.Second
)
var (
@@ -66,6 +68,7 @@ var (
ErrNoBlockchainSpec = errors.New("no blockchain spec was provided")
ErrNoSubnetID = errors.New("subnetID is missing")
ErrNoElasticSubnetSpec = errors.New("no elastic subnet spec was provided")
ErrNoValidatorSpec = errors.New("no elastic subnet spec was provided")
)
type Config struct {
@@ -519,6 +522,72 @@ func (s *server) CreateBlockchains(
return &rpcpb.CreateBlockchainsResponse{ClusterInfo: clusterInfo, ChainIds: strChainIDs}, nil
}
func (s *server) AddPermissionlessValidator(
_ context.Context,
req *rpcpb.AddPermissionlessValidatorRequest,
) (*rpcpb.AddPermissionlessValidatorResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.network == nil {
return nil, ErrNotBootstrapped
}
s.log.Debug("AddPermissionlessValidator")
if len(req.GetValidatorSpec()) == 0 {
return nil, ErrNoValidatorSpec
}
validatorSpecList := []network.PermissionlessValidatorSpec{}
for _, spec := range req.GetValidatorSpec() {
validatorSpec, err := getPermissionlessValidatorSpec(spec)
if err != nil {
return nil, err
}
validatorSpecList = append(validatorSpecList, validatorSpec)
}
// check that the given subnets exist
subnetsSet := set.Set[string]{}
subnetsSet.Add(maps.Keys(s.clusterInfo.Subnets)...)
for _, validatorSpec := range validatorSpecList {
if validatorSpec.SubnetID == nil {
return nil, ErrNoSubnetID
} else if !subnetsSet.Contains(*validatorSpec.SubnetID) {
return nil, fmt.Errorf("subnet id %q does not exist", *validatorSpec.SubnetID)
}
}
s.clusterInfo.Healthy = false
s.clusterInfo.CustomChainsHealthy = false
ctx, cancel := context.WithTimeout(context.Background(), waitForHealthyTimeout)
defer cancel()
txIDs, err := s.network.AddPermissionlessValidator(ctx, validatorSpecList)
s.updateClusterInfo()
if err != nil {
s.log.Error("failed to add permissionless validator", zap.Error(err))
return nil, err
}
s.log.Info("successfully added permissionless validator")
strTXIDs := []string{}
for _, txID := range txIDs {
strTXIDs = append(strTXIDs, txID.String())
}
clusterInfo, err := deepCopy(s.clusterInfo)
if err != nil {
return nil, err
}
return &rpcpb.AddPermissionlessValidatorResponse{ClusterInfo: clusterInfo, TxIds: strTXIDs}, nil
}
func (s *server) TransformElasticSubnets(
_ context.Context,
req *rpcpb.TransformElasticSubnetsRequest,
@@ -1257,6 +1326,29 @@ func getNetworkElasticSubnetSpec(
return elasticSubnetSpec
}
func getPermissionlessValidatorSpec(
spec *rpcpb.PermissionlessValidatorSpec,
) (network.PermissionlessValidatorSpec, error) {
startTime, err := time.Parse(timeParseLayout, spec.StartTime)
if err != nil {
return network.PermissionlessValidatorSpec{}, err
}
if startTime.Before(time.Now().Add(stakingMinimumLeadTime)) {
return network.PermissionlessValidatorSpec{}, fmt.Errorf("time should be at least %s in the future for validator spec of %s", stakingMinimumLeadTime, spec.NodeId)
}
stakeDuration := time.Duration(spec.StakeDuration) * time.Hour
validatorSpec := network.PermissionlessValidatorSpec{
SubnetID: &spec.SubnetId,
AssetID: spec.AssetId,
NodeID: spec.NodeId,
StakedAmount: spec.StakedTokenAmount,
StartTime: startTime,
StakeDuration: stakeDuration,
}
return validatorSpec, nil
}
func getNetworkBlockchainSpec(
log logging.Logger,
spec *rpcpb.BlockchainSpec,