github.com/MetalBlockchain/metalgo@v1.11.9/vms/manager.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package vms 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "sync" 11 12 "golang.org/x/exp/maps" 13 14 "github.com/MetalBlockchain/metalgo/ids" 15 "github.com/MetalBlockchain/metalgo/snow/engine/common" 16 "github.com/MetalBlockchain/metalgo/utils/logging" 17 ) 18 19 var ( 20 ErrNotFound = errors.New("not found") 21 22 _ Manager = (*manager)(nil) 23 ) 24 25 // A Factory creates new instances of a VM 26 type Factory interface { 27 New(logging.Logger) (interface{}, error) 28 } 29 30 // Manager tracks a collection of VM factories, their aliases, and their 31 // versions. 32 // It has the following functionality: 33 // 34 // 1. Register a VM factory. To register a VM is to associate its ID with a 35 // VMFactory which, when New() is called upon it, creates a new instance of 36 // that VM. 37 // 2. Get a VM factory. Given the ID of a VM that has been registered, return 38 // the factory that the ID is associated with. 39 // 3. Manage the aliases of VMs 40 // 4. Manage the versions of VMs 41 type Manager interface { 42 ids.Aliaser 43 44 // Return a factory that can create new instances of the vm whose ID is 45 // [vmID] 46 GetFactory(vmID ids.ID) (Factory, error) 47 48 // Map [vmID] to [factory]. [factory] creates new instances of the vm whose 49 // ID is [vmID] 50 RegisterFactory(ctx context.Context, vmID ids.ID, factory Factory) error 51 52 // ListFactories returns all the IDs that have had factories registered. 53 ListFactories() ([]ids.ID, error) 54 55 // Versions returns the primary alias of the VM mapped to the reported 56 // version of the VM for all the registered VMs that reported versions. 57 Versions() (map[string]string, error) 58 } 59 60 type manager struct { 61 // Note: The string representation of a VM's ID is also considered to be an 62 // alias of the VM. That is, [vmID].String() is an alias for [vmID]. 63 ids.Aliaser 64 65 log logging.Logger 66 67 lock sync.RWMutex 68 69 // Key: A VM's ID 70 // Value: A factory that creates new instances of that VM 71 factories map[ids.ID]Factory 72 73 // Key: A VM's ID 74 // Value: version the VM returned 75 versions map[ids.ID]string 76 } 77 78 // NewManager returns an instance of a VM manager 79 func NewManager(log logging.Logger, aliaser ids.Aliaser) Manager { 80 return &manager{ 81 Aliaser: aliaser, 82 log: log, 83 factories: make(map[ids.ID]Factory), 84 versions: make(map[ids.ID]string), 85 } 86 } 87 88 func (m *manager) GetFactory(vmID ids.ID) (Factory, error) { 89 m.lock.RLock() 90 defer m.lock.RUnlock() 91 92 if factory, ok := m.factories[vmID]; ok { 93 return factory, nil 94 } 95 return nil, fmt.Errorf("%q was %w", vmID, ErrNotFound) 96 } 97 98 func (m *manager) RegisterFactory(ctx context.Context, vmID ids.ID, factory Factory) error { 99 m.lock.Lock() 100 defer m.lock.Unlock() 101 102 if _, exists := m.factories[vmID]; exists { 103 return fmt.Errorf("%q was already registered as a vm", vmID) 104 } 105 if err := m.Alias(vmID, vmID.String()); err != nil { 106 return err 107 } 108 109 m.factories[vmID] = factory 110 111 vm, err := factory.New(m.log) 112 if err != nil { 113 return err 114 } 115 116 commonVM, ok := vm.(common.VM) 117 if !ok { 118 return nil 119 } 120 121 version, err := commonVM.Version(ctx) 122 if err != nil { 123 // Drop the shutdown error to surface the original error 124 _ = commonVM.Shutdown(ctx) 125 return err 126 } 127 128 m.versions[vmID] = version 129 return commonVM.Shutdown(ctx) 130 } 131 132 func (m *manager) ListFactories() ([]ids.ID, error) { 133 m.lock.RLock() 134 defer m.lock.RUnlock() 135 136 return maps.Keys(m.factories), nil 137 } 138 139 func (m *manager) Versions() (map[string]string, error) { 140 m.lock.RLock() 141 defer m.lock.RUnlock() 142 143 versions := make(map[string]string, len(m.versions)) 144 for vmID, version := range m.versions { 145 alias, err := m.PrimaryAlias(vmID) 146 if err != nil { 147 return nil, err 148 } 149 versions[alias] = version 150 } 151 return versions, nil 152 }