Skip to content

Commit

Permalink
Add platform.getSubnet API (#2704)
Browse files Browse the repository at this point in the history
Signed-off-by: felipemadero <felipe.madero@gmail.com>
Co-authored-by: Stephen Buttolph <stephen@avalabs.org>
Co-authored-by: Alberto Benegiamo <alberto.benegiamo@gmail.com>
  • Loading branch information
3 people committed Feb 14, 2024
1 parent a759c24 commit 2bfa6e8
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 17 deletions.
36 changes: 36 additions & 0 deletions vms/platformvm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ type Client interface {
startUTXOID ids.ID,
options ...rpc.Option,
) ([][]byte, ids.ShortID, ids.ID, error)
// GetSubnet returns information about the specified subnet
GetSubnet(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (GetSubnetClientResponse, error)
// GetSubnets returns information about the specified subnets
//
// Deprecated: Subnets should be fetched from a dedicated indexer.
Expand Down Expand Up @@ -381,6 +383,40 @@ func (c *client) GetAtomicUTXOs(
return utxos, endAddr, endUTXOID, err
}

// GetSubnetClientResponse is the response from calling GetSubnet on the client
type GetSubnetClientResponse struct {
// whether it is permissioned or not
IsPermissioned bool
// subnet auth information for a permissioned subnet
ControlKeys []ids.ShortID
Threshold uint32
Locktime uint64
// subnet transformation tx ID for a permissionless subnet
SubnetTransformationTxID ids.ID
}

func (c *client) GetSubnet(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (GetSubnetClientResponse, error) {
res := &GetSubnetResponse{}
err := c.requester.SendRequest(ctx, "platform.getSubnet", &GetSubnetArgs{
SubnetID: subnetID,
}, res, options...)
if err != nil {
return GetSubnetClientResponse{}, err
}
controlKeys, err := address.ParseToIDs(res.ControlKeys)
if err != nil {
return GetSubnetClientResponse{}, err
}

return GetSubnetClientResponse{
IsPermissioned: res.IsPermissioned,
ControlKeys: controlKeys,
Threshold: uint32(res.Threshold),
Locktime: uint64(res.Locktime),
SubnetTransformationTxID: res.SubnetTransformationTxID,
}, nil
}

// ClientSubnet is a representation of a subnet used in client methods
type ClientSubnet struct {
// ID of the subnet
Expand Down
102 changes: 85 additions & 17 deletions vms/platformvm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,23 @@ const (
)

var (
errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks")
errNoSubnetID = errors.New("argument 'subnetID' not provided")
errNoRewardAddress = errors.New("argument 'rewardAddress' not provided")
errInvalidDelegationRate = errors.New("argument 'delegationFeeRate' must be between 0 and 100, inclusive")
errNoAddresses = errors.New("no addresses provided")
errNoKeys = errors.New("user has no keys or funds")
errStartTimeTooSoon = fmt.Errorf("start time must be at least %s in the future", minAddStakerDelay)
errStartTimeTooLate = errors.New("start time is too far in the future")
errNamedSubnetCantBePrimary = errors.New("subnet validator attempts to validate primary network")
errNoAmount = errors.New("argument 'amount' must be > 0")
errMissingName = errors.New("argument 'name' not given")
errMissingVMID = errors.New("argument 'vmID' not given")
errMissingBlockchainID = errors.New("argument 'blockchainID' not given")
errMissingPrivateKey = errors.New("argument 'privateKey' not given")
errStartAfterEndTime = errors.New("start time must be before end time")
errStartTimeInThePast = errors.New("start time in the past")
errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks")
errNoSubnetID = errors.New("argument 'subnetID' not provided")
errPrimaryNetworkIsNotASubnet = errors.New("the primary network isn't a subnet")
errNoRewardAddress = errors.New("argument 'rewardAddress' not provided")
errInvalidDelegationRate = errors.New("argument 'delegationFeeRate' must be between 0 and 100, inclusive")
errNoAddresses = errors.New("no addresses provided")
errNoKeys = errors.New("user has no keys or funds")
errStartTimeTooSoon = fmt.Errorf("start time must be at least %s in the future", minAddStakerDelay)
errStartTimeTooLate = errors.New("start time is too far in the future")
errNamedSubnetCantBePrimary = errors.New("subnet validator attempts to validate primary network")
errNoAmount = errors.New("argument 'amount' must be > 0")
errMissingName = errors.New("argument 'name' not given")
errMissingVMID = errors.New("argument 'vmID' not given")
errMissingBlockchainID = errors.New("argument 'blockchainID' not given")
errMissingPrivateKey = errors.New("argument 'privateKey' not given")
errStartAfterEndTime = errors.New("start time must be before end time")
errStartTimeInThePast = errors.New("start time in the past")
)

// Service defines the API calls that can be made to the platform chain
Expand Down Expand Up @@ -509,6 +510,73 @@ func (s *Service) GetUTXOs(_ *http.Request, args *api.GetUTXOsArgs, response *ap
return nil
}

// GetSubnetArgs are the arguments to GetSubnet
type GetSubnetArgs struct {
// ID of the subnet to retrieve information about
SubnetID ids.ID `json:"subnetID"`
}

// GetSubnetResponse is the response from calling GetSubnet
type GetSubnetResponse struct {
// whether it is permissioned or not
IsPermissioned bool `json:"isPermissioned"`
// subnet auth information for a permissioned subnet
ControlKeys []string `json:"controlKeys"`
Threshold avajson.Uint32 `json:"threshold"`
Locktime avajson.Uint64 `json:"locktime"`
// subnet transformation tx ID for a permissionless subnet
SubnetTransformationTxID ids.ID `json:"subnetTransformationTxID"`
}

func (s *Service) GetSubnet(_ *http.Request, args *GetSubnetArgs, response *GetSubnetResponse) error {
s.vm.ctx.Log.Debug("API called",
zap.String("service", "platform"),
zap.String("method", "getSubnet"),
zap.Stringer("subnetID", args.SubnetID),
)

if args.SubnetID == constants.PrimaryNetworkID {
return errPrimaryNetworkIsNotASubnet
}

s.vm.ctx.Lock.Lock()
defer s.vm.ctx.Lock.Unlock()

subnetOwner, err := s.vm.state.GetSubnetOwner(args.SubnetID)
if err != nil {
return err
}
owner, ok := subnetOwner.(*secp256k1fx.OutputOwners)
if !ok {
return fmt.Errorf("expected *secp256k1fx.OutputOwners but got %T", subnetOwner)
}
controlAddrs := make([]string, len(owner.Addrs))
for i, controlKeyID := range owner.Addrs {
addr, err := s.addrManager.FormatLocalAddress(controlKeyID)
if err != nil {
return fmt.Errorf("problem formatting address: %w", err)
}
controlAddrs[i] = addr
}

response.ControlKeys = controlAddrs
response.Threshold = avajson.Uint32(owner.Threshold)
response.Locktime = avajson.Uint64(owner.Locktime)

switch subnetTransformationTx, err := s.vm.state.GetSubnetTransformation(args.SubnetID); err {
case nil:
response.IsPermissioned = false
response.SubnetTransformationTxID = subnetTransformationTx.ID()
case database.ErrNotFound:
response.IsPermissioned = true
response.SubnetTransformationTxID = ids.Empty
default:
return err
}

return nil
}

/*
******************************************************
******************* Get Subnets **********************
Expand All @@ -527,7 +595,7 @@ type APISubnet struct {
Threshold avajson.Uint32 `json:"threshold"`
}

// GetSubnetsArgs are the arguments to GetSubnet
// GetSubnetsArgs are the arguments to GetSubnets
type GetSubnetsArgs struct {
// IDs of the subnets to retrieve information about
// If omitted, gets all subnets
Expand Down

0 comments on commit 2bfa6e8

Please sign in to comment.