Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atomic vm wrapper #758

Closed
wants to merge 11 commits into from
Closed

Conversation

ceyonur
Copy link
Collaborator

@ceyonur ceyonur commented Jan 20, 2025

Why this should be merged

This adds a way to extend the inner EVM and wraps it with atomic VM. For demonstration it sets the network codec with atomic codec.

How this works

This pull request introduces significant changes to the plugin/evm package to improve the extensibility and modularity of the EVM (Ethereum Virtual Machine) implementation. The primary focus is on creating a new VM wrapper and updating initialization procedures to support network codec management. Here are the most important changes:

Unexported Shared EVM

  • Renamed and unexported the Shared EVM (plugin/evm/vm.go) and provided constructors.

New VM Wrapper and Initialization

  • plugin/evm/atomic/vm/vm.go: Introduced a new VM wrapper that implements multiple interfaces (secp256k1fx.VM, block.ChainVM, block.BuildBlockWithContextChainVM, block.StateSyncableVM). Added the WrapVM function to wrap an InnerVM and the Initialize method to set up the network codec.

Factory Updates

  • plugin/evm/factory.go: Modified the Factory struct to return the new VM wrapper in the New and NewPlugin methods, allowing the EVM to be used as a plugin with the appropriate network codec setup. [1] [2]

Codec Management

  • plugin/evm/vm.go: Removed the global networkCodec variable and added a networkCodec field to the VM struct. Implemented the SetNetworkCodec method to manage the codec. Updated various methods to use the networkCodec field instead of the global variable. [1] [2] [3] [4] [5] [6] [7] [8]

Test Updates

Main Function Update

  • plugin/main.go: Updated the main function to use the new Factory struct for creating the EVM plugin, ensuring the proper setup of the network codec.

How this was tested

Existing UTs should cover, but might be refactored.

Need to be documented?

No

Need to update RELEASES.md?

No

plugin/evm/vm.go Outdated
// VM implements the snowman.ChainVM interface
type VM struct {
// sharedEvm implements the snowman.ChainVM interface
type sharedEvm struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we put this back? "VM" seems sufficient and as the wrapped VM is in another package it seems that we can leave this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont want to export this as without default extensions it will break. we can check if required extensions are nil in initialize to make sure it won't break if we want to. but I still slightly think we should not directly expose vm.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smaller API / less exported is always a good thing imo, so happy to keep this unexported. Now yeah it's more not-necessarily-necessary git diffs, but fine with me.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think sharedEvm is a good name here (what is it shared between? runtime? compile time?) Perhaps we can use a more descriptive name or vm?

I don't disagree that unexported is better, but renaming this seems like it should be its own PR as it is not necessary for this PR and makes this PR difficult to follow.

}

// Initialize implements the snowman.ChainVM interface
func (vm *VM) Initialize(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly instead of plugin/evm/atomic/vm, we can have plugin/evm/coreth which defines the VM for use in avalanchego.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think we probably would want to have a coreth/ and subnet-evm/ packages in the merged repo. For now I did not want to break the avalanchego.

atomicTxs []*atomic.Tx
}

// newBlock returns a new Block wrapping the ethBlock type and implementing the snowman.Block interface
func (vm *VM) newBlock(ethBlock *types.Block) (*Block, error) {
func (vm *sharedEvm) newBlock(ethBlock *types.Block) (*Block, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume some further changes to block.go is needed to fully have the atomic vm separated from the "base" evm that we can share with subnet-evm?

plugin/evm/atomic/vm/vm.go Outdated Show resolved Hide resolved
plugin/evm/atomic/vm/vm.go Outdated Show resolved Hide resolved
profiler profiler.Profiler
}

func NewAdminService(vm *VM, performanceDir string) *Admin {
func NewAdminService(vm *sharedEvm, performanceDir string) *Admin {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be an unexported function. Otherwise the vm argument type should either be exported or be an exported interface (preferrable). But in this case, NewAdminService is only used once in the same package, I'd just unexport it.

type Factory struct{}

func (*Factory) New(logging.Logger) (interface{}, error) {
return &VM{}, nil
return atomicvm.WrapVM(NewExtensibleEVM(false)), nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supernit: you can use constants to indicate what the bool argument is, it's nice for readability instead of having to hover over the function to check what the argument is / check the fn in code when PR reviewing on the github website

Suggested change
return atomicvm.WrapVM(NewExtensibleEVM(false)), nil
const isPlugin = false
return atomicvm.WrapVM(NewExtensibleEVM(isPlugin)), nil

Same for NewPlugin below 😉

plugin/evm/vm.go Outdated
Comment on lines 311 to 313
// NewDefaultEVM returns a new instance of the VM with default extensions
// This should not be called if the VM is being extended
func NewDefaultEVM() *sharedEvm {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is only used in tests, move it to a _test.go file? You could have a helpers_test.go since it's used in various test files. Unless we want to use elsewhere 🤔
And then you can pass in a t *testing.T and use require.NoError(t, err) for error checks.

If this is meant to be used in production code, we really need to return an error I think? Even if it's mean to be in production code later, I'd rather have it in test code and then migrated to production code when it's used in production code at a later time.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved it to test for now, alternatively we can check these extension points in Initialize if they're nil, and if so we can provide some defaults in the vm.Initialize() directly.

plugin/evm/vm.go Outdated Show resolved Hide resolved
plugin/evm/vm.go Outdated
return vm
}

func NewExtensibleEVM(isPlugin bool) *sharedEvm {
Copy link
Collaborator

@qdm12 qdm12 Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unexport this since it's only used in plugin/evm

If you want to export it for user by other packages, then export sharedEVM (aka re-use older VM name and reduce diffs at the same time)

plugin/evm/vm.go Outdated Show resolved Hide resolved
Comment on lines +66 to +74
var networkCodec codec.Manager

func init() {
var err error
networkCodec, err = message.NewCodec(message.BlockSyncSummary{})
if err != nil {
panic(err)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having this at global scope with an init block, you could have the networkCodec created each time it's needed (in 5 places below in this same file) with

networkCodec, err := message.NewCodec(message.BlockSyncSummary{})
require.NoError(t, err)

It's always better to avoid global state / init blocks if possible.

Comment on lines 22 to 23
// TODO: either move this from plugin or move the VM itself
type Factory struct{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we use a factory after all actually? It looks like it's only used in plugin/main.go as

	rpcchainvm.Serve(context.Background(), factory.NewPlugin())

It makes no sense to have a factory as far as I understood 🤔 ?

which could well be replaced with

	factory := evm.Factory{}
	rpcchainvm.Serve(context.Background(), evm.NewPlugin())

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ceyonur and others added 7 commits January 21, 2025 12:56
Co-authored-by: Quentin McGaw <[email protected]>
Signed-off-by: Ceyhun Onur <[email protected]>
Co-authored-by: Quentin McGaw <[email protected]>
Signed-off-by: Ceyhun Onur <[email protected]>
Co-authored-by: Quentin McGaw <[email protected]>
Signed-off-by: Ceyhun Onur <[email protected]>
Co-authored-by: Quentin McGaw <[email protected]>
Signed-off-by: Ceyhun Onur <[email protected]>
plugin/evm/vm.go Outdated
// VM implements the snowman.ChainVM interface
type VM struct {
// sharedEvm implements the snowman.ChainVM interface
type sharedEvm struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think sharedEvm is a good name here (what is it shared between? runtime? compile time?) Perhaps we can use a more descriptive name or vm?

I don't disagree that unexported is better, but renaming this seems like it should be its own PR as it is not necessary for this PR and makes this PR difficult to follow.

plugin/evm/vm.go Outdated
@@ -314,14 +308,26 @@ type VM struct {
rpcHandlers []interface{ Stop() }
}

func newExtensibleEVM(isPlugin bool) *sharedEvm {
Copy link
Collaborator

@darioush darioush Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we have a function like newVM that takes the codec.Manager and drop the SetNetworkCodec function below?

It seems this type is not fully initialized even if it is constructed with a "new" function.

We can pass the "additional extension points" in to the "new" function (either as separate arguments or as a config struct), as all the customization should be known at construction time (i.e., is this VM coreth or subnet-evm).

@ceyonur ceyonur closed this Feb 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants