diff --git a/README.md b/README.md index 340cc6e717..0539ecbc52 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,11 @@ Services are created by adding an [Option](./vm/option.go) to the VM. They can b ```golang // NewWithOptions returns a VM with the specified options func New(options ...vm.Option) (*vm.VM, error) { - options = append(options, With(), indexer.With()) // Add MorpheusVM API and Indexer - return vm.New( + return defaultvm.New( consts.Version, genesis.DefaultGenesisFactory{}, &storage.StateManager{}, - ActionParser, - AuthParser, + newRegistryFactory(), auth.Engines(), options..., ) diff --git a/chain/dependencies.go b/chain/dependencies.go index 3884670a4b..3276b073a7 100644 --- a/chain/dependencies.go +++ b/chain/dependencies.go @@ -287,3 +287,5 @@ type AuthFactory interface { MaxUnits() (bandwidth uint64, compute uint64) Address() codec.Address } + +type RegistryFactory func() (actionRegistry ActionRegistry, authRegistry AuthRegistry, outputRegistry OutputRegistry) diff --git a/docs/tutorials/morpheusvm/morpheusvm.md b/docs/tutorials/morpheusvm/morpheusvm.md index 97b227a690..41de217b8b 100644 --- a/docs/tutorials/morpheusvm/morpheusvm.md +++ b/docs/tutorials/morpheusvm/morpheusvm.md @@ -771,36 +771,35 @@ import ( "github.com/ava-labs/hypersdk/examples/tutorial/actions" ) -var ( - ActionParser *codec.TypeParser[chain.Action] - AuthParser *codec.TypeParser[chain.Auth] - OutputParser *codec.TypeParser[codec.Typed] -) - -// Setup types -func init() { - ActionParser = codec.NewTypeParser[chain.Action]() - AuthParser = codec.NewTypeParser[chain.Auth]() - OutputParser = codec.NewTypeParser[codec.Typed]() +func newRegistryFactory() chain.RegistryFactory { + actionParser := codec.NewTypeParser[chain.Action]() + authParser := codec.NewTypeParser[chain.Auth]() + outputParser := codec.NewTypeParser[codec.Typed]() errs := &wrappers.Errs{} errs.Add( - ActionParser.Register(&actions.Transfer{}, actions.UnmarshalTransfer), + // When registering new actions, ALWAYS make sure to append at the end. + // Pass nil as second argument if manual marshalling isn't needed (if in doubt, you probably don't) + actionParser.Register(&actions.Transfer{}, actions.UnmarshalTransfer), - AuthParser.Register(&auth.ED25519{}, auth.UnmarshalED25519), - AuthParser.Register(&auth.SECP256R1{}, auth.UnmarshalSECP256R1), - AuthParser.Register(&auth.BLS{}, auth.UnmarshalBLS), + // When registering new auth, ALWAYS make sure to append at the end. + authParser.Register(&auth.ED25519{}, auth.UnmarshalED25519), + authParser.Register(&auth.SECP256R1{}, auth.UnmarshalSECP256R1), + authParser.Register(&auth.BLS{}, auth.UnmarshalBLS), - OutputParser.Register(&actions.TransferResult{}, nil), + outputParser.Register(&actions.TransferResult{}, nil), ) if errs.Errored() { panic(errs.Err) } + return func() (actionRegistry chain.ActionRegistry, authRegistry chain.AuthRegistry, outputRegistry chain.OutputRegistry) { + return actionParser, authParser, outputParser + } } ``` -By “registry”, we mean the `ActionParser` and `AuthParser` which tell our VM +By “registry”, we mean the `actionParser` and `authParser` which tell our VM which actions and cryptographic functions that it’ll support. Finally, we create a `New()` function that allows for the VM we’ve worked on to @@ -813,9 +812,7 @@ func New(options ...vm.Option) (*vm.VM, error) { consts.Version, genesis.DefaultGenesisFactory{}, &storage.StateManager{}, - ActionParser, - AuthParser, - OutputParser, + newRegistryFactory(), auth.Engines(), options..., ) diff --git a/docs/tutorials/morpheusvm/options.md b/docs/tutorials/morpheusvm/options.md index 53fc743f67..e5f8a86953 100644 --- a/docs/tutorials/morpheusvm/options.md +++ b/docs/tutorials/morpheusvm/options.md @@ -254,22 +254,26 @@ var _ chain.Parser = (*Parser)(nil) type Parser struct { genesis *genesis.DefaultGenesis + registryFactory chain.RegistryFactory } func (p *Parser) Rules(_ int64) chain.Rules { return p.genesis.Rules } -func (*Parser) ActionRegistry() chain.ActionRegistry { - return ActionParser +func (p *Parser) ActionRegistry() chain.ActionRegistry { + actionRegistry, _, _ := p.registryFactory() + return actionRegistry } -func (*Parser) OutputRegistry() chain.OutputRegistry { - return OutputParser +func (*Parser) AuthRegistry() chain.AuthRegistry { + _, authRegistry, _ := p.registryFactory() + return authRegistry } -func (*Parser) AuthRegistry() chain.AuthRegistry { - return AuthParser +func (*Parser) OutputRegistry() chain.OutputRegistry { + _, _, outputRegistry := p.registryFactory() + return outputRegistry } func (*Parser) StateManager() chain.StateManager { @@ -277,7 +281,7 @@ func (*Parser) StateManager() chain.StateManager { } func NewParser(genesis *genesis.DefaultGenesis) chain.Parser { - return &Parser{genesis: genesis} + return &Parser{genesis: genesis, registryFactory: newRegistryFactory()} } // Used as a lambda function for creating ExternalSubscriberServer parser diff --git a/examples/morpheusvm/tests/e2e/e2e_test.go b/examples/morpheusvm/tests/e2e/e2e_test.go index 85c5816c7d..f383818cea 100644 --- a/examples/morpheusvm/tests/e2e/e2e_test.go +++ b/examples/morpheusvm/tests/e2e/e2e_test.go @@ -42,7 +42,10 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { genesisBytes, err := json.Marshal(gen) require.NoError(err) - expectedABI, err := abi.NewABI(vm.ActionParser.GetRegisteredTypes(), vm.OutputParser.GetRegisteredTypes()) + parser, err := vm.CreateParser(genesisBytes) + require.NoError(err) + + expectedABI, err := abi.NewABI((*parser.ActionRegistry()).GetRegisteredTypes(), (*parser.OutputRegistry()).GetRegisteredTypes()) require.NoError(err) // Import HyperSDK e2e test coverage and inject MorpheusVM name diff --git a/examples/morpheusvm/tests/workload/workload.go b/examples/morpheusvm/tests/workload/workload.go index 77a821ce5d..c51f7d4807 100644 --- a/examples/morpheusvm/tests/workload/workload.go +++ b/examples/morpheusvm/tests/workload/workload.go @@ -245,7 +245,9 @@ func confirmTx(ctx context.Context, require *require.Assertions, uri string, txI transferOutputBytes := []byte(txRes.Outputs[0]) require.Equal(consts.TransferID, transferOutputBytes[0]) reader := codec.NewReader(transferOutputBytes, len(transferOutputBytes)) - transferOutputTyped, err := vm.OutputParser.Unmarshal(reader) + parser, err := lcli.Parser(context.Background()) + require.NoError(err) + transferOutputTyped, err := (*parser.OutputRegistry()).Unmarshal(reader) require.NoError(err) transferOutput, ok := transferOutputTyped.(*actions.TransferResult) require.True(ok) diff --git a/examples/morpheusvm/vm/client.go b/examples/morpheusvm/vm/client.go index ff086c7c8f..8b43451967 100644 --- a/examples/morpheusvm/vm/client.go +++ b/examples/morpheusvm/vm/client.go @@ -99,23 +99,27 @@ func (cli *JSONRPCClient) Parser(ctx context.Context) (chain.Parser, error) { var _ chain.Parser = (*Parser)(nil) type Parser struct { - genesis *genesis.DefaultGenesis + genesis *genesis.DefaultGenesis + registryFactory chain.RegistryFactory } func (p *Parser) Rules(_ int64) chain.Rules { return p.genesis.Rules } -func (*Parser) ActionRegistry() chain.ActionRegistry { - return ActionParser +func (p *Parser) ActionRegistry() chain.ActionRegistry { + actionRegistry, _, _ := p.registryFactory() + return actionRegistry } -func (*Parser) OutputRegistry() chain.OutputRegistry { - return OutputParser +func (p *Parser) AuthRegistry() chain.AuthRegistry { + _, authRegistry, _ := p.registryFactory() + return authRegistry } -func (*Parser) AuthRegistry() chain.AuthRegistry { - return AuthParser +func (p *Parser) OutputRegistry() chain.OutputRegistry { + _, _, outputRegistry := p.registryFactory() + return outputRegistry } func (*Parser) StateManager() chain.StateManager { @@ -123,7 +127,7 @@ func (*Parser) StateManager() chain.StateManager { } func NewParser(genesis *genesis.DefaultGenesis) chain.Parser { - return &Parser{genesis: genesis} + return &Parser{genesis: genesis, registryFactory: newRegistryFactory()} } // Used as a lambda function for creating ExternalSubscriberServer parser diff --git a/examples/morpheusvm/vm/vm.go b/examples/morpheusvm/vm/vm.go index ee3d56e2b1..fa9676cf9e 100644 --- a/examples/morpheusvm/vm/vm.go +++ b/examples/morpheusvm/vm/vm.go @@ -17,34 +17,30 @@ import ( "github.com/ava-labs/hypersdk/vm/defaultvm" ) -var ( - ActionParser *codec.TypeParser[chain.Action] - AuthParser *codec.TypeParser[chain.Auth] - OutputParser *codec.TypeParser[codec.Typed] -) - -// Setup types -func init() { - ActionParser = codec.NewTypeParser[chain.Action]() - AuthParser = codec.NewTypeParser[chain.Auth]() - OutputParser = codec.NewTypeParser[codec.Typed]() +func newRegistryFactory() chain.RegistryFactory { + actionParser := codec.NewTypeParser[chain.Action]() + authParser := codec.NewTypeParser[chain.Auth]() + outputParser := codec.NewTypeParser[codec.Typed]() errs := &wrappers.Errs{} errs.Add( // When registering new actions, ALWAYS make sure to append at the end. // Pass nil as second argument if manual marshalling isn't needed (if in doubt, you probably don't) - ActionParser.Register(&actions.Transfer{}, actions.UnmarshalTransfer), + actionParser.Register(&actions.Transfer{}, actions.UnmarshalTransfer), // When registering new auth, ALWAYS make sure to append at the end. - AuthParser.Register(&auth.ED25519{}, auth.UnmarshalED25519), - AuthParser.Register(&auth.SECP256R1{}, auth.UnmarshalSECP256R1), - AuthParser.Register(&auth.BLS{}, auth.UnmarshalBLS), + authParser.Register(&auth.ED25519{}, auth.UnmarshalED25519), + authParser.Register(&auth.SECP256R1{}, auth.UnmarshalSECP256R1), + authParser.Register(&auth.BLS{}, auth.UnmarshalBLS), - OutputParser.Register(&actions.TransferResult{}, nil), + outputParser.Register(&actions.TransferResult{}, nil), ) if errs.Errored() { panic(errs.Err) } + return func() (actionRegistry chain.ActionRegistry, authRegistry chain.AuthRegistry, outputRegistry chain.OutputRegistry) { + return actionParser, authParser, outputParser + } } // NewWithOptions returns a VM with the specified options @@ -54,9 +50,7 @@ func New(options ...vm.Option) (*vm.VM, error) { consts.Version, genesis.DefaultGenesisFactory{}, &storage.StateManager{}, - ActionParser, - AuthParser, - OutputParser, + newRegistryFactory(), auth.Engines(), options..., ) diff --git a/examples/vmwithcontracts/tests/e2e/e2e_test.go b/examples/vmwithcontracts/tests/e2e/e2e_test.go index e843913ead..4cd4ad1be0 100644 --- a/examples/vmwithcontracts/tests/e2e/e2e_test.go +++ b/examples/vmwithcontracts/tests/e2e/e2e_test.go @@ -42,7 +42,10 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { genesisBytes, err := json.Marshal(gen) require.NoError(err) - expectedABI, err := abi.NewABI(vm.ActionParser.GetRegisteredTypes(), vm.OutputParser.GetRegisteredTypes()) + parser, err := vm.CreateParser(genesisBytes) + require.NoError(err) + + expectedABI, err := abi.NewABI((*parser.ActionRegistry()).GetRegisteredTypes(), (*parser.OutputRegistry()).GetRegisteredTypes()) require.NoError(err) // Import HyperSDK e2e test coverage and inject VMWithContracts name diff --git a/examples/vmwithcontracts/vm/client.go b/examples/vmwithcontracts/vm/client.go index c75e8e821f..75c51dca11 100644 --- a/examples/vmwithcontracts/vm/client.go +++ b/examples/vmwithcontracts/vm/client.go @@ -102,23 +102,27 @@ func (cli *JSONRPCClient) Parser(ctx context.Context) (chain.Parser, error) { var _ chain.Parser = (*Parser)(nil) type Parser struct { - genesis *genesis.DefaultGenesis + genesis *genesis.DefaultGenesis + registryFactory chain.RegistryFactory } func (p *Parser) Rules(_ int64) chain.Rules { return p.genesis.Rules } -func (*Parser) ActionRegistry() chain.ActionRegistry { - return ActionParser +func (p *Parser) ActionRegistry() chain.ActionRegistry { + actionRegistry, _, _ := p.registryFactory() + return actionRegistry } -func (*Parser) OutputRegistry() chain.OutputRegistry { - return OutputParser +func (p *Parser) AuthRegistry() chain.AuthRegistry { + _, authRegistry, _ := p.registryFactory() + return authRegistry } -func (*Parser) AuthRegistry() chain.AuthRegistry { - return AuthParser +func (p *Parser) OutputRegistry() chain.OutputRegistry { + _, _, outputRegistry := p.registryFactory() + return outputRegistry } func (*Parser) StateManager() chain.StateManager { @@ -126,7 +130,7 @@ func (*Parser) StateManager() chain.StateManager { } func NewParser(genesis *genesis.DefaultGenesis) chain.Parser { - return &Parser{genesis: genesis} + return &Parser{genesis: genesis, registryFactory: newRegistryFactory()} } // Used as a lambda function for creating ExternalSubscriberServer parser diff --git a/examples/vmwithcontracts/vm/vm.go b/examples/vmwithcontracts/vm/vm.go index 3aadbf7e30..c87723d838 100644 --- a/examples/vmwithcontracts/vm/vm.go +++ b/examples/vmwithcontracts/vm/vm.go @@ -21,39 +21,36 @@ import ( "github.com/ava-labs/hypersdk/x/contracts/runtime" ) -var ( - ActionParser *codec.TypeParser[chain.Action] - AuthParser *codec.TypeParser[chain.Auth] - OutputParser *codec.TypeParser[codec.Typed] - wasmRuntime *runtime.WasmRuntime -) +var wasmRuntime *runtime.WasmRuntime -// Setup types -func init() { - ActionParser = codec.NewTypeParser[chain.Action]() - AuthParser = codec.NewTypeParser[chain.Auth]() - OutputParser = codec.NewTypeParser[codec.Typed]() +func newRegistryFactory() chain.RegistryFactory { + actionParser := codec.NewTypeParser[chain.Action]() + authParser := codec.NewTypeParser[chain.Auth]() + outputParser := codec.NewTypeParser[codec.Typed]() errs := &wrappers.Errs{} errs.Add( // When registering new actions, ALWAYS make sure to append at the end. // Pass nil as second argument if manual marshalling isn't needed (if in doubt, you probably don't) - ActionParser.Register(&actions.Transfer{}, actions.UnmarshalTransfer), - ActionParser.Register(&actions.Call{}, actions.UnmarshalCallContract(wasmRuntime)), - ActionParser.Register(&actions.Publish{}, actions.UnmarshalPublishContract), - ActionParser.Register(&actions.Deploy{}, actions.UnmarshalDeployContract), + actionParser.Register(&actions.Transfer{}, actions.UnmarshalTransfer), + actionParser.Register(&actions.Call{}, actions.UnmarshalCallContract(wasmRuntime)), + actionParser.Register(&actions.Publish{}, actions.UnmarshalPublishContract), + actionParser.Register(&actions.Deploy{}, actions.UnmarshalDeployContract), // When registering new auth, ALWAYS make sure to append at the end. - AuthParser.Register(&auth.ED25519{}, auth.UnmarshalED25519), - AuthParser.Register(&auth.SECP256R1{}, auth.UnmarshalSECP256R1), - AuthParser.Register(&auth.BLS{}, auth.UnmarshalBLS), + authParser.Register(&auth.ED25519{}, auth.UnmarshalED25519), + authParser.Register(&auth.SECP256R1{}, auth.UnmarshalSECP256R1), + authParser.Register(&auth.BLS{}, auth.UnmarshalBLS), - OutputParser.Register(&actions.Result{}, nil), - OutputParser.Register(&actions.AddressOutput{}, nil), + outputParser.Register(&actions.Result{}, nil), + outputParser.Register(&actions.AddressOutput{}, nil), ) if errs.Errored() { panic(errs.Err) } + return func() (actionRegistry chain.ActionRegistry, authRegistry chain.AuthRegistry, outputRegistry chain.OutputRegistry) { + return actionParser, authParser, outputParser + } } // New returns a VM with the indexer, websocket, rpc, and external subscriber apis enabled. @@ -78,9 +75,7 @@ func NewWithOptions(options ...vm.Option) (*vm.VM, error) { consts.Version, genesis.DefaultGenesisFactory{}, &storage.StateManager{}, - ActionParser, - AuthParser, - OutputParser, + newRegistryFactory(), auth.Engines(), opts..., ) diff --git a/vm/defaultvm/vm.go b/vm/defaultvm/vm.go index 3ee7d30f1a..4e0a799596 100644 --- a/vm/defaultvm/vm.go +++ b/vm/defaultvm/vm.go @@ -35,9 +35,7 @@ func New( v *version.Semantic, genesisFactory genesis.GenesisAndRuleFactory, stateManager chain.StateManager, - actionRegistry chain.ActionRegistry, - authRegistry chain.AuthRegistry, - outputRegistry chain.OutputRegistry, + registryFactory chain.RegistryFactory, authEngine map[uint8]vm.AuthEngine, options ...vm.Option, ) (*vm.VM, error) { @@ -46,9 +44,7 @@ func New( v, genesisFactory, stateManager, - actionRegistry, - authRegistry, - outputRegistry, + registryFactory, authEngine, options..., ) diff --git a/vm/vm.go b/vm/vm.go index 22b4a2a4f5..657068722f 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -150,12 +150,12 @@ func New( v *version.Semantic, genesisFactory genesis.GenesisAndRuleFactory, stateManager chain.StateManager, - actionRegistry chain.ActionRegistry, - authRegistry chain.AuthRegistry, - outputRegistry chain.OutputRegistry, + registryFactory chain.RegistryFactory, authEngine map[uint8]AuthEngine, options ...Option, ) (*VM, error) { + actionRegistry, authRegistry, outputRegistry := registryFactory() + allocatedNamespaces := set.NewSet[string](len(options)) for _, option := range options { if allocatedNamespaces.Contains(option.Namespace) {