mirror of https://github.com/poanetwork/gecko.git
139 lines
4.1 KiB
Go
139 lines
4.1 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package vms
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/ava-labs/gecko/api"
|
|
"github.com/ava-labs/gecko/ids"
|
|
"github.com/ava-labs/gecko/snow"
|
|
"github.com/ava-labs/gecko/snow/engine/common"
|
|
"github.com/ava-labs/gecko/utils/logging"
|
|
)
|
|
|
|
// A VMFactory creates new instances of a VM
|
|
type VMFactory interface {
|
|
New(*snow.Context) (interface{}, error)
|
|
}
|
|
|
|
// Manager is a VM manager.
|
|
// It has the following functionality:
|
|
// 1) Register a VM factory. To register a VM is to associate its ID with a
|
|
// VMFactory which, when New() is called upon it, creates a new instance of that VM.
|
|
// 2) Get a VM factory. Given the ID of a VM that has been
|
|
// registered, return the factory that the ID is associated with.
|
|
// 3) Associate a VM with an alias
|
|
// 4) Get the ID of the VM by the VM's alias
|
|
// 5) Get the aliases of a VM
|
|
type Manager interface {
|
|
// Returns a factory that can create new instances of the VM
|
|
// with the given ID
|
|
GetVMFactory(ids.ID) (VMFactory, error)
|
|
|
|
// Associate an ID with the factory that creates new instances
|
|
// of the VM with the given ID
|
|
RegisterVMFactory(ids.ID, VMFactory) error
|
|
|
|
// Given an alias, return the ID of the VM associated with that alias
|
|
Lookup(string) (ids.ID, error)
|
|
|
|
// Return the aliases associated with a VM
|
|
Aliases(ids.ID) []string
|
|
|
|
// Give an alias to a VM
|
|
Alias(ids.ID, string) error
|
|
}
|
|
|
|
// Implements Manager
|
|
type manager struct {
|
|
// Note: The string representation of a VM's ID is also considered to be an
|
|
// alias of the VM. That is, [VM].String() is an alias for the VM, too.
|
|
ids.Aliaser
|
|
|
|
// Key: The key underlying a VM's ID
|
|
// Value: A factory that creates new instances of that VM
|
|
vmFactories map[[32]byte]VMFactory
|
|
|
|
// The node's API server.
|
|
// [manager] adds routes to this server to expose new API endpoints/services
|
|
apiServer *api.Server
|
|
|
|
log logging.Logger
|
|
}
|
|
|
|
// NewManager returns an instance of a VM manager
|
|
func NewManager(apiServer *api.Server, log logging.Logger) Manager {
|
|
m := &manager{
|
|
vmFactories: make(map[[32]byte]VMFactory),
|
|
apiServer: apiServer,
|
|
log: log,
|
|
}
|
|
m.Initialize()
|
|
return m
|
|
}
|
|
|
|
// Return a factory that can create new instances of the vm whose
|
|
// ID is [vmID]
|
|
func (m *manager) GetVMFactory(vmID ids.ID) (VMFactory, error) {
|
|
if factory, ok := m.vmFactories[vmID.Key()]; ok {
|
|
return factory, nil
|
|
}
|
|
return nil, fmt.Errorf("no vm with ID '%v' has been registered", vmID)
|
|
|
|
}
|
|
|
|
// Map [vmID] to [factory]. [factory] creates new instances of the vm whose
|
|
// ID is [vmID]
|
|
func (m *manager) RegisterVMFactory(vmID ids.ID, factory VMFactory) error {
|
|
key := vmID.Key()
|
|
if _, exists := m.vmFactories[key]; exists {
|
|
return fmt.Errorf("a vm with ID '%v' has already been registered", vmID)
|
|
}
|
|
if err := m.Alias(vmID, vmID.String()); err != nil {
|
|
return err
|
|
}
|
|
|
|
m.vmFactories[key] = factory
|
|
|
|
// add the static API endpoints
|
|
m.addStaticAPIEndpoints(vmID)
|
|
return nil
|
|
}
|
|
|
|
// VMs can expose a static API (one that does not depend on the state of a particular chain.)
|
|
// This method adds to the node's API server the static API of the VM with ID [vmID].
|
|
// This allows clients to call the VM's static API methods.
|
|
func (m *manager) addStaticAPIEndpoints(vmID ids.ID) {
|
|
vmFactory, err := m.GetVMFactory(vmID)
|
|
m.log.AssertNoError(err)
|
|
m.log.Debug("adding static API for VM with ID %s", vmID)
|
|
vm, err := vmFactory.New(nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
staticVM, ok := vm.(common.StaticVM)
|
|
if !ok {
|
|
staticVM, ok := vm.(common.VM)
|
|
if ok {
|
|
staticVM.Shutdown()
|
|
}
|
|
return
|
|
}
|
|
|
|
// all static endpoints go to the vm endpoint, defaulting to the vm id
|
|
defaultEndpoint := "vm/" + vmID.String()
|
|
// use a single lock for this entire vm
|
|
lock := new(sync.RWMutex)
|
|
// register the static endpoints
|
|
for extension, service := range staticVM.CreateStaticHandlers() {
|
|
m.log.Verbo("adding static API endpoint: %s", defaultEndpoint+extension)
|
|
if err := m.apiServer.AddRoute(service, lock, defaultEndpoint, extension, m.log); err != nil {
|
|
m.log.Warn("failed to add static API endpoint %s: %v", fmt.Sprintf("%s%s", defaultEndpoint, extension), err)
|
|
}
|
|
}
|
|
}
|