Skip to content

Commit d8b5710

Browse files
authored
Merge pull request #678 from axone-protocol/refactor/logic-vfs
Refactor/logic vfs
2 parents bcffe2f + 2c41f3c commit d8b5710

20 files changed

+916
-373
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ mock: ## Generate all the mocks (for tests)
377377
@mockgen -source=x/vesting/types/expected_keepers.go -package testutil -destination x/vesting/testutil/expected_keepers_mocks.go
378378
@mockgen -source=x/logic/types/expected_keepers.go -package testutil -destination x/logic/testutil/expected_keepers_mocks.go
379379
@mockgen -destination x/logic/testutil/gas_mocks.go -package testutil cosmossdk.io/store/types GasMeter
380+
@mockgen -destination x/logic/testutil/fs_mocks.go -package testutil io/fs FS
381+
@mockgen -destination x/logic/testutil/read_file_fs_mocks.go -package testutil io/fs ReadFileFS
380382

381383
## Release:
382384
.PHONY: release-assets

app/app.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ import (
139139
axonewasm "github.com/axone-protocol/axoned/v8/app/wasm"
140140
"github.com/axone-protocol/axoned/v8/docs"
141141
logicmodule "github.com/axone-protocol/axoned/v8/x/logic"
142-
logicfs "github.com/axone-protocol/axoned/v8/x/logic/fs"
142+
"github.com/axone-protocol/axoned/v8/x/logic/fs/composite"
143+
wasm2 "github.com/axone-protocol/axoned/v8/x/logic/fs/wasm"
143144
logicmodulekeeper "github.com/axone-protocol/axoned/v8/x/logic/keeper"
144145
logicmoduletypes "github.com/axone-protocol/axoned/v8/x/logic/types"
145146
"github.com/axone-protocol/axoned/v8/x/mint"
@@ -1161,6 +1162,9 @@ func (app *App) SimulationManager() *module.SimulationManager {
11611162
// provideFS is used to provide the virtual file system used for the logic module
11621163
// to load external file.
11631164
func (app *App) provideFS(ctx context.Context) fs.FS {
1164-
wasmHandler := logicfs.NewWasmHandler(app.WasmKeeper)
1165-
return logicfs.NewVirtualFS(ctx, []logicfs.URIHandler{wasmHandler})
1165+
vfs := composite.NewFS()
1166+
1167+
vfs.Mount(wasm2.Scheme, wasm2.NewFS(ctx, app.WasmKeeper))
1168+
1169+
return vfs
11661170
}

x/logic/fs/composite/fs.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package composite
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io"
7+
"io/fs"
8+
"net/url"
9+
"sort"
10+
11+
"golang.org/x/exp/maps"
12+
)
13+
14+
type FS interface {
15+
fs.ReadFileFS
16+
17+
// Mount mounts a filesystem to the given mount point.
18+
// The mount point is the scheme of the URI.
19+
Mount(mountPoint string, fs fs.FS)
20+
// ListMounts returns a list of all mount points in sorted order.
21+
ListMounts() []string
22+
}
23+
24+
type vfs struct {
25+
mounted map[string]fs.FS
26+
}
27+
28+
var (
29+
_ fs.FS = (*vfs)(nil)
30+
_ fs.ReadFileFS = (*vfs)(nil)
31+
)
32+
33+
func NewFS() FS {
34+
return &vfs{mounted: make(map[string]fs.FS)}
35+
}
36+
37+
func (f *vfs) Open(name string) (fs.File, error) {
38+
uri, err := f.validatePath(name)
39+
if err != nil {
40+
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
41+
}
42+
43+
vfs, err := f.resolve(uri)
44+
if err != nil {
45+
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
46+
}
47+
return vfs.Open(name)
48+
}
49+
50+
func (f *vfs) ReadFile(name string) ([]byte, error) {
51+
uri, err := f.validatePath(name)
52+
if err != nil {
53+
return nil, &fs.PathError{Op: "readfile", Path: name, Err: fs.ErrInvalid}
54+
}
55+
56+
vfs, err := f.resolve(uri)
57+
if err != nil {
58+
return nil, &fs.PathError{Op: "readfile", Path: name, Err: fs.ErrNotExist}
59+
}
60+
61+
if vfs, ok := vfs.(fs.ReadFileFS); ok {
62+
content, err := vfs.ReadFile(name)
63+
if err != nil {
64+
return nil, &fs.PathError{Op: "readfile", Path: name, Err: getUnderlyingError(err)}
65+
}
66+
67+
return content, nil
68+
}
69+
70+
file, err := vfs.Open(name)
71+
if err != nil {
72+
return nil, &fs.PathError{Op: "readfile", Path: name, Err: getUnderlyingError(err)}
73+
}
74+
defer file.Close()
75+
76+
content, err := io.ReadAll(file)
77+
if err != nil {
78+
return nil, &fs.PathError{Op: "readfile", Path: name, Err: getUnderlyingError(err)}
79+
}
80+
81+
return content, nil
82+
}
83+
84+
func (f *vfs) Mount(mountPoint string, fs fs.FS) {
85+
f.mounted[mountPoint] = fs
86+
}
87+
88+
func (f *vfs) ListMounts() []string {
89+
mounts := maps.Keys(f.mounted)
90+
sort.Strings(mounts)
91+
92+
return mounts
93+
}
94+
95+
// validatePath checks if the provided path is a valid URL and returns its URI.
96+
func (f *vfs) validatePath(name string) (*url.URL, error) {
97+
uri, err := url.Parse(name)
98+
if err != nil {
99+
return nil, err
100+
}
101+
if uri.Scheme == "" {
102+
return nil, fmt.Errorf("missing scheme in path: %s", name)
103+
}
104+
return uri, nil
105+
}
106+
107+
// resolve returns the filesystem mounted at the given URI scheme.
108+
func (f *vfs) resolve(uri *url.URL) (fs.FS, error) {
109+
vfs, ok := f.mounted[uri.Scheme]
110+
if !ok {
111+
return nil, fmt.Errorf("no filesystem mounted at: %s", uri.Scheme)
112+
}
113+
return vfs, nil
114+
}
115+
116+
// getUnderlyingError returns the underlying error of a path error.
117+
// If it's not a path error it returns the error itself.
118+
func getUnderlyingError(err error) error {
119+
var pathErr *fs.PathError
120+
if errors.As(err, &pathErr) {
121+
return pathErr.Err
122+
}
123+
return err
124+
}

0 commit comments

Comments
 (0)