Skip to content

Commit

Permalink
LFVM: Rest register experimental features into global registry (#870)
Browse files Browse the repository at this point in the history
* Use lowercase for errors

As recommended in best practices

* Test registration of experimental lfvm instances.

* Bump up coverage for lfvm.go
  • Loading branch information
LuisPH3 authored Oct 17, 2024
1 parent f89bf3e commit 4cd8e41
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .testcoverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ override:
path: go/interpreter/geth/ct.go
- threshold: 87
path: go/interpreter/lfvm/ct.go
- threshold: 37
- threshold: 100
path: go/interpreter/lfvm/lfvm.go
- threshold: 100
path: go/interpreter/lfvm/converter.go
Expand Down
45 changes: 22 additions & 23 deletions go/interpreter/lfvm/lfvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func init() {
// function should not be called in production code, as the resulting VMs are
// not officially supported.
func RegisterExperimentalInterpreterConfigurations() error {

configs := map[string]config{}

for _, si := range []string{"", "-si"} {
for _, shaCache := range []string{"", "-no-sha-cache"} {
for _, mode := range []string{"", "-stats", "-logging"} {
Expand All @@ -67,35 +70,31 @@ func RegisterExperimentalInterpreterConfigurations() error {
}

name := "lfvm" + si + shaCache + mode
if name != "lfvm" {
err := tosca.RegisterInterpreterFactory(
name,
func(any) (tosca.Interpreter, error) {
return newVm(config)
},
)
if err != nil {
return fmt.Errorf("failed to register interpreter %q: %v", name, err)
}
if name == "lfvm" {
continue
}

configs[name] = config
}
}
}

lfvmNoCodeCache := "lfvm-no-code-cache"
err := tosca.RegisterInterpreterFactory(
lfvmNoCodeCache,
func(any) (tosca.Interpreter, error) {
return newVm(config{
ConversionConfig: ConversionConfig{
CacheSize: -1,
},
})
},
)
if err != nil {
return fmt.Errorf("failed to register interpreter %q: %v", lfvmNoCodeCache, err)
configs["lfvm-no-code-cache"] = config{
ConversionConfig: ConversionConfig{CacheSize: -1},
}

for name, config := range configs {
err := tosca.RegisterInterpreterFactory(
name,
func(any) (tosca.Interpreter, error) {
return newVm(config)
},
)
if err != nil {
return fmt.Errorf("failed to register interpreter %q: %v", name, err)
}
}

return nil
}

Expand Down
72 changes: 72 additions & 0 deletions go/interpreter/lfvm/lfvm_interface_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2024 Fantom Foundation
//
// Use of this software is governed by the Business Source License included
// in the LICENSE file and at fantom.foundation/bsl11.
//
// Change Date: 2028-4-16
//
// On the date above, in accordance with the Business Source License, use of
// this software will be governed by the GNU Lesser General Public License v3.

package lfvm_test

import (
"testing"

"github.com/Fantom-foundation/Tosca/go/interpreter/lfvm"
"github.com/Fantom-foundation/Tosca/go/tosca"
)

// TestLfvm_RegisterExperimentalConfigurations tests the registration of
// experimental configurations.
// This test is slightly different from other tests because of dealing with the
// global registry:
// - It is declared in it's own package to avoid leaking the registration to other tests.
// - It tests different properties, in one single function. The reason is that the
// order of different functions may change, invalidating the test.
func TestLfvm_RegisterExperimentalConfigurations(t *testing.T) {

// Fist registration must succeed.
err := lfvm.RegisterExperimentalInterpreterConfigurations()
if err != nil {
t.Fatalf("failed to register experimental configurations: %v", err)
}

// Registering a second time must fail.
err = lfvm.RegisterExperimentalInterpreterConfigurations()
if err == nil {
t.Fatalf("expected error when registering experimental configurations twice")
}

// Check that lfvm is registered by default, in addition to experimental configurations
if _, ok := tosca.GetAllRegisteredInterpreters()["lfvm"]; !ok {
t.Fatalf("lfvm is not registered")
}

// Construct all registered interpreter configurations
for name, factory := range tosca.GetAllRegisteredInterpreters() {
t.Run(name, func(t *testing.T) {
vm, err := factory(lfvm.Config{})
if err != nil {
t.Fatalf("failed to create interpreter: %v", err)
}

// Vms are opaque, we can't check their configuration directly.
// We can only check that they do execute some basic code.
params := tosca.Parameters{}
params.Code = []byte{byte(lfvm.PUSH1), 0xff, byte(lfvm.POP), byte(lfvm.STOP)}
params.Gas = 5
res, err := vm.Run(params)
if err != nil {
t.Fatalf("failed to run interpreter: %v", err)
}

if want, got := true, res.Success; want != got {
t.Fatalf("unexpected success result: want %v, got %v", want, got)
}
if want, got := tosca.Gas(0), res.GasLeft; want != got {
t.Fatalf("unexpected gas used: want %v, got %v", want, got)
}
})
}
}
26 changes: 26 additions & 0 deletions go/interpreter/lfvm/lfvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package lfvm

import (
"fmt"
"testing"

"github.com/Fantom-foundation/Tosca/go/tosca"
Expand Down Expand Up @@ -45,3 +46,28 @@ func TestLfvm_OfficialConfigurationHasSanctionedProperties(t *testing.T) {
t.Fatalf("lfvm is configured with super instructions")
}
}

func TestLfvm_InterpreterReturnsErrorWhenExecutingUnsupportedRevision(t *testing.T) {
vm, err := tosca.NewInterpreter("lfvm")
if err != nil {
t.Fatalf("lfvm is not registered: %v", err)
}

params := tosca.Parameters{}
params.Revision = newestSupportedRevision + 1

_, err = vm.Run(params)
if want, got := fmt.Sprintf("unsupported revision %d", params.Revision), err.Error(); want != got {
t.Fatalf("unexpected error: want %q, got %q", want, got)
}
}

func TestLfvm_newVm_returnsErrorWithWrongConfiguration(t *testing.T) {
config := config{
ConversionConfig: ConversionConfig{CacheSize: maxCachedCodeLength / 2},
}
_, err := newVm(config)
if err == nil {
t.Fatalf("expected error, got nil")
}
}
2 changes: 1 addition & 1 deletion go/tosca/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ type ErrUnsupportedRevision struct {
}

func (e *ErrUnsupportedRevision) Error() string {
return fmt.Sprintf("Unsupported revision %d", e.Revision)
return fmt.Sprintf("unsupported revision %d", e.Revision)
}

// ProfilingInterpreter is an optional extension to the Interpreter interface
Expand Down

0 comments on commit 4cd8e41

Please sign in to comment.