Skip to content

Commit 28359b6

Browse files
authored
Merge pull request #673 from ava-labs/add-subnet-validator-new
Add subnet validators
2 parents a8b6471 + 146a8fc commit 28359b6

File tree

12 files changed

+1562
-911
lines changed

12 files changed

+1562
-911
lines changed

client/client.go

+10
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type Client interface {
3737
AddPermissionlessValidator(ctx context.Context, validatorSpec []*rpcpb.PermissionlessStakerSpec) (*rpcpb.AddPermissionlessValidatorResponse, error)
3838
AddPermissionlessDelegator(ctx context.Context, validatorSpec []*rpcpb.PermissionlessStakerSpec) (*rpcpb.AddPermissionlessDelegatorResponse, error)
3939
RemoveSubnetValidator(ctx context.Context, validatorSpec []*rpcpb.RemoveSubnetValidatorSpec) (*rpcpb.RemoveSubnetValidatorResponse, error)
40+
AddSubnetValidators(ctx context.Context, validatorSpec []*rpcpb.SubnetValidatorsSpec) (*rpcpb.AddSubnetValidatorsResponse, error)
4041
Health(ctx context.Context) (*rpcpb.HealthResponse, error)
4142
WaitForHealthy(ctx context.Context) (*rpcpb.WaitForHealthyResponse, error)
4243
URIs(ctx context.Context) ([]string, error)
@@ -203,6 +204,15 @@ func (c *client) RemoveSubnetValidator(ctx context.Context, validatorSpec []*rpc
203204
return c.controlc.RemoveSubnetValidator(ctx, req)
204205
}
205206

207+
func (c *client) AddSubnetValidators(ctx context.Context, validatorSpec []*rpcpb.SubnetValidatorsSpec) (*rpcpb.AddSubnetValidatorsResponse, error) {
208+
req := &rpcpb.AddSubnetValidatorsRequest{
209+
ValidatorsSpec: validatorSpec,
210+
}
211+
212+
c.log.Info("add subnet validators")
213+
return c.controlc.AddSubnetValidators(ctx, req)
214+
}
215+
206216
func (c *client) Health(ctx context.Context) (*rpcpb.HealthResponse, error) {
207217
c.log.Info("health")
208218
return c.controlc.Health(ctx, &rpcpb.HealthRequest{})

cmd/control/control.go

+39
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func NewCommand() *cobra.Command {
6363
newTransformElasticSubnetsCommand(),
6464
newAddPermissionlessValidatorCommand(),
6565
newAddPermissionlessDelegatorCommand(),
66+
newAddSubnetValidatorsCommand(),
6667
newRemoveSubnetValidatorCommand(),
6768
newHealthCommand(),
6869
newWaitForHealthyCommand(),
@@ -444,6 +445,16 @@ func newAddPermissionlessValidatorCommand() *cobra.Command {
444445
return cmd
445446
}
446447

448+
func newAddSubnetValidatorsCommand() *cobra.Command {
449+
cmd := &cobra.Command{
450+
Use: "add-subnet-validators validatorsSpec [options]",
451+
Short: "Adds subnet validators",
452+
RunE: addSubnetValidatorsFunc,
453+
Args: cobra.ExactArgs(1),
454+
}
455+
return cmd
456+
}
457+
447458
func newRemoveSubnetValidatorCommand() *cobra.Command {
448459
cmd := &cobra.Command{
449460
Use: "remove-subnet-validator removeValidatorSpec [options]",
@@ -538,6 +549,34 @@ func addPermissionlessValidatorFunc(_ *cobra.Command, args []string) error {
538549
return nil
539550
}
540551

552+
func addSubnetValidatorsFunc(_ *cobra.Command, args []string) error {
553+
cli, err := newClient()
554+
if err != nil {
555+
return err
556+
}
557+
defer cli.Close()
558+
559+
validatorSpecsStr := args[0]
560+
561+
validatorSpecs := []*rpcpb.SubnetValidatorsSpec{}
562+
if err := json.Unmarshal([]byte(validatorSpecsStr), &validatorSpecs); err != nil {
563+
return err
564+
}
565+
566+
ctx := getAsyncContext()
567+
568+
info, err := cli.AddSubnetValidators(
569+
ctx,
570+
validatorSpecs,
571+
)
572+
if err != nil {
573+
return err
574+
}
575+
576+
ux.Print(log, logging.Green.Wrap("add-subnet-validators response: %+v"), info)
577+
return nil
578+
}
579+
541580
func removeSubnetValidatorFunc(_ *cobra.Command, args []string) error {
542581
cli, err := newClient()
543582
if err != nil {

docs/examples.md

+19
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,25 @@ curl -X POST -k http://localhost:8081/v1/control/listsubnets
185185
avalanche-network-runner control list-subnets
186186
```
187187

188+
To add a node as a validator to a Subnet:
189+
190+
```sh
191+
curl -X POST -k http://localhost:8081/v1/control/addsubnetvalidators -d '[{"subnetId": "'$SUBNET_ID'", "nodeNames":["node1"]}]'
192+
193+
# or
194+
avalanche-network-runner control add-subnet-validators '[{"subnet_id": "'$SUBNET_ID'", "node_names":["node1"]}]'
195+
```
196+
197+
To remove a node as a validator from a Subnet:
198+
199+
```sh
200+
curl -X POST -k http://localhost:8081/v1/control/removesubnetvalidator -d '[{"subnetId": "'$SUBNET_ID'", "nodeNames":["node1"]}]'
201+
202+
# or
203+
avalanche-network-runner control remove-subnet-validator '[{"subnet_id": "'$SUBNET_ID'", "node_names":["node1"]}]'
204+
```
205+
206+
188207
## Operating with blockchains
189208

190209
**Note**: To create a blockchain, the vm binary for it should be present under the plugin dir, with a filename equal to the vm id. The plugin dir

docs/reference.md

+26
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,32 @@ curl --location 'http://localhost:8081/v1/control/addpermissionlessvalidator' \
172172
]}'
173173
```
174174

175+
## `add-subnet-validators`
176+
177+
Adds subnet validators
178+
179+
### Usage
180+
181+
```sh
182+
avalanche-network-runner control add-subnet-validators validatorsSpec [options] [flags]
183+
```
184+
185+
### Example
186+
187+
cli
188+
189+
```sh
190+
avalanche-network-runner control add-subnet-validators '[{"subnet_id": "p433wpuXyJiDhyazPYyZMJeaoPSW76CBZ2x7wrVPLgvokotXz", "node_names":["node1"]}]'
191+
```
192+
193+
curl
194+
195+
```sh
196+
curl --location 'http://localhost:8081/v1/control/addsubnetvalidators' \
197+
--header 'Content-Type: application/json' \
198+
--data '[{"subnetId": "p433wpuXyJiDhyazPYyZMJeaoPSW76CBZ2x7wrVPLgvokotXz", "nodeNames":["node1"]}]'
199+
200+
175201
## `attach-peer`
176202

177203
Attaches a peer to the node.

local/blockchain.go

+99-7
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,24 @@ func (ln *localNetwork) RegisterBlockchainAliases(
154154
return nil
155155
}
156156

157+
func (ln *localNetwork) AddSubnetValidators(
158+
ctx context.Context,
159+
subnetSpecs []network.SubnetValidatorsSpec,
160+
) error {
161+
ln.lock.Lock()
162+
defer ln.lock.Unlock()
163+
164+
return ln.addSubnetValidators(ctx, subnetSpecs)
165+
}
166+
157167
func (ln *localNetwork) RemoveSubnetValidators(
158168
ctx context.Context,
159-
removeSubnetSpecs []network.RemoveSubnetValidatorSpec,
169+
subnetSpecs []network.SubnetValidatorsSpec,
160170
) error {
161171
ln.lock.Lock()
162172
defer ln.lock.Unlock()
163173

164-
return ln.removeSubnetValidators(ctx, removeSubnetSpecs)
174+
return ln.removeSubnetValidators(ctx, subnetSpecs)
165175
}
166176

167177
func (ln *localNetwork) AddPermissionlessValidators(
@@ -342,7 +352,7 @@ func (ln *localNetwork) installCustomChains(
342352
return nil, err
343353
}
344354

345-
if err = ln.addSubnetValidators(ctx, platformCli, w, subnetIDs, subnetSpecs); err != nil {
355+
if err = ln.issueSubnetValidatorTxs(ctx, platformCli, w, subnetIDs, subnetSpecs); err != nil {
346356
return nil, err
347357
}
348358

@@ -409,6 +419,88 @@ func (ln *localNetwork) installCustomChains(
409419
return chainInfos, nil
410420
}
411421

422+
func (ln *localNetwork) addSubnetValidators(
423+
ctx context.Context,
424+
subnetValidatorsSpecs []network.SubnetValidatorsSpec,
425+
) error {
426+
ln.log.Info(logging.Blue.Wrap(logging.Bold.Wrap("add subnet validators")))
427+
428+
clientURI, err := ln.getClientURI()
429+
if err != nil {
430+
return err
431+
}
432+
platformCli := platformvm.NewClient(clientURI)
433+
434+
// wallet needs txs for all previously created subnets
435+
subnetIDs := make([]ids.ID, len(subnetValidatorsSpecs))
436+
for i, spec := range subnetValidatorsSpecs {
437+
subnetID, err := ids.FromString(spec.SubnetID)
438+
if err != nil {
439+
return err
440+
}
441+
subnetIDs[i] = subnetID
442+
}
443+
w, err := newWallet(ctx, clientURI, subnetIDs)
444+
if err != nil {
445+
return err
446+
}
447+
448+
for _, spec := range subnetValidatorsSpecs {
449+
if len(spec.NodeNames) == 0 {
450+
return fmt.Errorf("no validators provided for subnet %s", spec.SubnetID)
451+
}
452+
}
453+
454+
// create new nodes
455+
for _, spec := range subnetValidatorsSpecs {
456+
for _, nodeName := range spec.NodeNames {
457+
_, ok := ln.nodes[nodeName]
458+
if !ok {
459+
ln.log.Info(logging.Green.Wrap(fmt.Sprintf("adding new participant %s", nodeName)))
460+
if _, err := ln.addNode(node.Config{
461+
Name: nodeName,
462+
RedirectStdout: ln.redirectStdout,
463+
RedirectStderr: ln.redirectStderr,
464+
}); err != nil {
465+
return err
466+
}
467+
}
468+
}
469+
}
470+
if err := ln.healthy(ctx); err != nil {
471+
return err
472+
}
473+
474+
// just ensure all nodes are primary validators (so can be subnet validators)
475+
if err := ln.addPrimaryValidators(ctx, platformCli, w); err != nil {
476+
return err
477+
}
478+
479+
// wait for nodes to be primary validators before trying to add them as subnet ones
480+
if err = ln.waitPrimaryValidators(ctx, platformCli); err != nil {
481+
return err
482+
}
483+
484+
subnetSpecs := []network.SubnetSpec{}
485+
for _, spec := range subnetValidatorsSpecs {
486+
subnetSpecs = append(subnetSpecs, network.SubnetSpec{Participants: spec.NodeNames})
487+
}
488+
489+
if err = ln.issueSubnetValidatorTxs(ctx, platformCli, w, subnetIDs, subnetSpecs); err != nil {
490+
return err
491+
}
492+
493+
if err := ln.restartNodes(ctx, subnetIDs, subnetSpecs, nil, nil, nil); err != nil {
494+
return err
495+
}
496+
497+
if err = ln.waitSubnetValidators(ctx, platformCli, subnetIDs, subnetSpecs); err != nil {
498+
return err
499+
}
500+
501+
return nil
502+
}
503+
412504
func (ln *localNetwork) installSubnets(
413505
ctx context.Context,
414506
subnetSpecs []network.SubnetSpec,
@@ -479,7 +571,7 @@ func (ln *localNetwork) installSubnets(
479571
return nil, err
480572
}
481573

482-
if err = ln.addSubnetValidators(ctx, platformCli, w, subnetIDs, subnetSpecs); err != nil {
574+
if err = ln.issueSubnetValidatorTxs(ctx, platformCli, w, subnetIDs, subnetSpecs); err != nil {
483575
return nil, err
484576
}
485577

@@ -586,7 +678,7 @@ func (ln *localNetwork) restartNodes(
586678
subnetIDs []ids.ID,
587679
subnetSpecs []network.SubnetSpec,
588680
validatorSpecs []network.PermissionlessStakerSpec,
589-
removeValidatorSpecs []network.RemoveSubnetValidatorSpec,
681+
removeValidatorSpecs []network.SubnetValidatorsSpec,
590682
nodesToRestartForBlockchainConfigUpdate set.Set[string],
591683
) (err error) {
592684
if (subnetSpecs != nil && validatorSpecs != nil) || (subnetSpecs != nil && removeValidatorSpecs != nil) ||
@@ -879,7 +971,7 @@ func importPChainFromXChain(ctx context.Context, w *wallet, owner *secp256k1fx.O
879971

880972
func (ln *localNetwork) removeSubnetValidators(
881973
ctx context.Context,
882-
removeSubnetSpecs []network.RemoveSubnetValidatorSpec,
974+
removeSubnetSpecs []network.SubnetValidatorsSpec,
883975
) error {
884976
ln.log.Info("removing subnet validator tx")
885977
removeSubnetSpecIDs := make([]ids.ID, len(removeSubnetSpecs))
@@ -1314,7 +1406,7 @@ func createSubnets(
13141406
// add the nodes in subnet participant as validators of the given subnets, in case they are not
13151407
// the validation starts as soon as possible and its duration is as long as possible, that is,
13161408
// it ends at the time the primary network validation ends for the node
1317-
func (ln *localNetwork) addSubnetValidators(
1409+
func (ln *localNetwork) issueSubnetValidatorTxs(
13181410
ctx context.Context,
13191411
platformCli platformvm.Client,
13201412
w *wallet,

network/network.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ type SubnetSpec struct {
4747
SubnetConfig []byte
4848
}
4949

50-
type RemoveSubnetValidatorSpec struct {
50+
type SubnetValidatorsSpec struct {
5151
NodeNames []string
5252
SubnetID string
5353
}
@@ -120,7 +120,9 @@ type Network interface {
120120
// Add a validator into an elastic subnet
121121
AddPermissionlessValidators(context.Context, []PermissionlessStakerSpec) error
122122
// Remove a validator from a subnet
123-
RemoveSubnetValidators(context.Context, []RemoveSubnetValidatorSpec) error
123+
RemoveSubnetValidators(context.Context, []SubnetValidatorsSpec) error
124+
// Add a validator toa subnet
125+
AddSubnetValidators(context.Context, []SubnetValidatorsSpec) error
124126
// Get the elastic subnet tx id for the given subnet id
125127
GetElasticSubnetID(context.Context, ids.ID) (ids.ID, error)
126128
}

0 commit comments

Comments
 (0)