diff --git a/.golangci.yml b/.golangci.yml index 5ae0a0463..10609a2b4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,6 +2,7 @@ version: "2" run: tests: true + timeout: 5m linters: # Enable specific linters @@ -10,6 +11,38 @@ linters: - misspell - testifylint - thelper + - copyloopvar # Detect copy loops + - errcheck # Detect unchecked errors + - govet # Reports suspicious constructs + - ineffassign # Detect unused assignments + - staticcheck # Go static analysis + - unused # Detect unused constants, variables, functions and types + + # Additional recommended linters + - gosec # Security checker + - misspell # Find commonly misspelled words + - bodyclose # Check HTTP response bodies are closed + - goconst # Find repeated strings that could be constants + - gocognit # Check cognitive complexity + - whitespace # Check trailing whitespace + - thelper # Detect test helpers not using t.Helper() + - tparallel # Detect incorrect usage of t.Parallel() + + # enable lager because changes are huge + # - revive + # - gocritic # A more opinionated linter + + + settings: + gocritic: + enable-all: true + disabled-checks: + - dupSubExpr + - dupImport + - paramTypeCombine + + + exclusions: generated: lax presets: diff --git a/cmd/demo/main.go b/cmd/demo/main.go index 85742d938..c31813552 100644 --- a/cmd/demo/main.go +++ b/cmd/demo/main.go @@ -4,53 +4,91 @@ import ( "fmt" "math" "os" + "path/filepath" + "strings" wasmvm "github.com/CosmWasm/wasmvm/v3" ) +// PrintDebug enables debug printing when true. const ( - PRINT_DEBUG = true - MEMORY_LIMIT = 32 // MiB - CACHE_SIZE = 100 // MiB + PrintDebug = true + // MemoryLimit defines the memory limit in MiB. + MemoryLimit = 32 + // CacheSize defines the cache size in MiB. + CacheSize = 100 ) -var SUPPORTED_CAPABILITIES = []string{"staking"} +// SupportedCapabilities defines the list of supported staking capabilities. +var SupportedCapabilities = []string{"staking"} -// This is just a demo to ensure we can compile a static go binary +// exitCode tracks the code that the program will exit with. +var exitCode = 0 + +// main is the entry point for the demo application that tests wasmvm functionality. func main() { + defer func() { + os.Exit(exitCode) + }() + + if len(os.Args) < 2 { + fmt.Println("Usage: demo ") + exitCode = 1 + return + } + file := os.Args[1] if file == "version" { libwasmvmVersion, err := wasmvm.LibwasmvmVersion() if err != nil { - panic(err) + fmt.Printf("Error getting libwasmvm version: %v\n", err) + exitCode = 1 + return } fmt.Printf("libwasmvm: %s\n", libwasmvmVersion) return } fmt.Printf("Running %s...\n", file) - bz, err := os.ReadFile(file) + + // Validate file path + cleanPath := filepath.Clean(file) + if filepath.IsAbs(cleanPath) || strings.Contains(cleanPath, "..") { + fmt.Println("Error: invalid file path") + exitCode = 1 + return + } + + bz, err := os.ReadFile(cleanPath) if err != nil { - panic(err) + fmt.Printf("Error reading file: %v\n", err) + exitCode = 1 + return } fmt.Println("Loaded!") - err = os.MkdirAll("tmp", 0o755) + err = os.MkdirAll("tmp", 0o750) if err != nil { - panic(err) + fmt.Printf("Error creating tmp directory: %v\n", err) + exitCode = 1 + return } - vm, err := wasmvm.NewVM("tmp", SUPPORTED_CAPABILITIES, MEMORY_LIMIT, PRINT_DEBUG, CACHE_SIZE) + vm, err := wasmvm.NewVM("tmp", SupportedCapabilities, MemoryLimit, PrintDebug, CacheSize) if err != nil { - panic(err) + fmt.Printf("Error creating VM: %v\n", err) + exitCode = 1 + return } + defer vm.Cleanup() checksum, _, err := vm.StoreCode(bz, math.MaxUint64) if err != nil { - panic(err) + fmt.Printf("Error storing code: %v\n", err) + exitCode = 1 + return } fmt.Printf("Stored code with checksum: %X\n", checksum) - vm.Cleanup() fmt.Println("finished") } diff --git a/ibc_test.go b/ibc_test.go index 265116792..653b09701 100644 --- a/ibc_test.go +++ b/ibc_test.go @@ -70,7 +70,7 @@ type AccountInfo struct { ChannelID string `json:"channel_id"` } -// We just check if an error is returned or not +// We just check if an error is returned or not. type AcknowledgeDispatch struct { Err string `json:"error"` } diff --git a/internal/api/callbacks.go b/internal/api/callbacks.go index 012ce7c58..2d1b502d0 100644 --- a/internal/api/callbacks.go +++ b/internal/api/callbacks.go @@ -158,7 +158,7 @@ func cGet(ptr *C.db_t, gasMeter *C.gas_meter_t, usedGas *cu64, key C.U8SliceView return C.GoError_BadArgument } // errOut is unused and we don't check `is_none` because of https://github.com/CosmWasm/wasmvm/issues/536 - if !(*val).is_none { + if !val.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } @@ -231,7 +231,7 @@ func cScan(ptr *C.db_t, gasMeter *C.gas_meter_t, usedGas *cu64, start C.U8SliceV // we received an invalid pointer return C.GoError_BadArgument } - if !(*errOut).is_none { + if !errOut.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } @@ -277,14 +277,13 @@ func cNext(ref C.IteratorReference, gasMeter *C.gas_meter_t, usedGas *cu64, key // k, v := itr.Key(); itr.Value() // ... // } - defer recoverPanic(&ret) if ref.call_id == 0 || gasMeter == nil || usedGas == nil || key == nil || val == nil || errOut == nil { // we received an invalid pointer return C.GoError_BadArgument } // errOut is unused and we don't check `is_none` because of https://github.com/CosmWasm/wasmvm/issues/536 - if !(*key).is_none || !(*val).is_none { + if !key.is_none || !val.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } @@ -336,7 +335,7 @@ func nextPart(ref C.IteratorReference, gasMeter *C.gas_meter_t, usedGas *cu64, o return C.GoError_BadArgument } // errOut is unused and we don't check `is_none` because of https://github.com/CosmWasm/wasmvm/issues/536 - if !(*output).is_none { + if !output.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } @@ -356,7 +355,7 @@ func nextPart(ref C.IteratorReference, gasMeter *C.gas_meter_t, usedGas *cu64, o // check iter.Error() ???? iter.Next() gasAfter := gm.GasConsumed() - *usedGas = (cu64)(gasAfter - gasBefore) + *usedGas = (cu64(gasAfter - gasBefore)) *output = newUnmanagedVector(out) return C.GoError_None @@ -384,7 +383,7 @@ func cHumanizeAddress(ptr *C.api_t, src C.U8SliceView, dest *C.UnmanagedVector, if dest == nil || errOut == nil { return C.GoError_BadArgument } - if !(*dest).is_none || !(*errOut).is_none { + if !dest.is_none || !errOut.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } @@ -398,7 +397,7 @@ func cHumanizeAddress(ptr *C.api_t, src C.U8SliceView, dest *C.UnmanagedVector, *errOut = newUnmanagedVector([]byte(err.Error())) return C.GoError_User } - if len(h) == 0 { + if h == "" { panic(fmt.Sprintf("`api.HumanizeAddress()` returned an empty string for %q", s)) } *dest = newUnmanagedVector([]byte(h)) @@ -412,7 +411,7 @@ func cCanonicalizeAddress(ptr *C.api_t, src C.U8SliceView, dest *C.UnmanagedVect if dest == nil || errOut == nil { return C.GoError_BadArgument } - if !(*dest).is_none || !(*errOut).is_none { + if !dest.is_none || !errOut.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } @@ -439,7 +438,7 @@ func cValidateAddress(ptr *C.api_t, src C.U8SliceView, errOut *C.UnmanagedVector if errOut == nil { return C.GoError_BadArgument } - if !(*errOut).is_none { + if !errOut.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } @@ -479,7 +478,7 @@ func cQueryExternal(ptr *C.querier_t, gasLimit cu64, usedGas *cu64, request C.U8 // we received an invalid pointer return C.GoError_BadArgument } - if !(*result).is_none || !(*errOut).is_none { + if !result.is_none || !errOut.is_none { panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") } diff --git a/internal/api/iterator.go b/internal/api/iterator.go index 32e408136..7ddbb49f2 100644 --- a/internal/api/iterator.go +++ b/internal/api/iterator.go @@ -3,12 +3,13 @@ package api import ( "fmt" "math" + "os" "sync" "github.com/CosmWasm/wasmvm/v3/types" ) -// frame stores all Iterators for one contract call +// frame stores all Iterators for one contract call. type frame []types.Iterator // iteratorFrames contains one frame for each contract call, indexed by contract call ID. @@ -17,7 +18,7 @@ var ( iteratorFramesMutex sync.Mutex ) -// this is a global counter for creating call IDs +// this is a global counter for creating call IDs. var ( latestCallID uint64 latestCallIDMutex sync.Mutex @@ -44,13 +45,17 @@ func removeFrame(callID uint64) frame { return remove } -// endCall is called at the end of a contract call to remove one item the iteratorFrames +// endCall is called at the end of a contract call to remove one item the iteratorFrames. func endCall(callID uint64) { // we pull removeFrame in another function so we don't hold the mutex while cleaning up the removed frame remove := removeFrame(callID) // free all iterators in the frame when we release it for _, iter := range remove { - iter.Close() + if err := iter.Close(); err != nil { + // In this cleanup code, we can't do much with the error + // Log it to stderr since that's better than silently ignoring it + fmt.Fprintf(os.Stderr, "failed to close iterator: %v\n", err) + } } } diff --git a/internal/api/lib.go b/internal/api/lib.go index b4da90396..42eeda915 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -44,7 +44,7 @@ type Cache struct { type Querier = types.Querier func InitCache(config types.VMConfig) (Cache, error) { - // libwasmvm would create this directory too but we need it earlier for the lockfile + // libwasmvm would create this directory too but we need it earlier for the lockfile. err := os.MkdirAll(config.Cache.BaseDir, 0o755) if err != nil { return Cache{}, fmt.Errorf("could not create base directory") @@ -83,7 +83,10 @@ func InitCache(config types.VMConfig) (Cache, error) { func ReleaseCache(cache Cache) { C.release_cache(cache.ptr) - cache.lockfile.Close() // Also releases the file lock + if err := cache.lockfile.Close(); err != nil { + // Just log the error since this is cleanup code + fmt.Printf("failed to close lockfile: %v\n", err) + } // Also releases the file lock. } func StoreCode(cache Cache, wasm []byte, persist bool) ([]byte, error) { diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index 6b11e3faa..17c322f80 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -47,7 +47,13 @@ func TestInitAndReleaseCache(t *testing.T) { // wasmd expects us to create the base directory // https://github.com/CosmWasm/wasmd/blob/v0.30.0/x/wasm/keeper/keeper.go#L128 func TestInitCacheWorksForNonExistentDir(t *testing.T) { - tmpdir := t.TempDir() + tmpdir, err := os.MkdirTemp("", "wasmvm-testing") + require.NoError(t, err) + defer func() { + if err := os.RemoveAll(tmpdir); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() createMe := filepath.Join(tmpdir, "does-not-yet-exist") config := types.VMConfig{ @@ -81,7 +87,13 @@ func TestInitCacheErrorsForBrokenDir(t *testing.T) { } func TestInitLockingPreventsConcurrentAccess(t *testing.T) { - tmpdir := t.TempDir() + tmpdir, err := os.MkdirTemp("", "wasmvm-testing") + require.NoError(t, err) + defer func() { + if err := os.RemoveAll(tmpdir); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() config1 := types.VMConfig{ Cache: types.CacheOptions{ @@ -122,9 +134,27 @@ func TestInitLockingPreventsConcurrentAccess(t *testing.T) { } func TestInitLockingAllowsMultipleInstancesInDifferentDirs(t *testing.T) { - tmpdir1 := t.TempDir() - tmpdir2 := t.TempDir() - tmpdir3 := t.TempDir() + tmpdir1, err := os.MkdirTemp("", "wasmvm-testing1") + require.NoError(t, err) + tmpdir2, err := os.MkdirTemp("", "wasmvm-testing2") + require.NoError(t, err) + tmpdir3, err := os.MkdirTemp("", "wasmvm-testing3") + require.NoError(t, err) + defer func() { + if err := os.RemoveAll(tmpdir1); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() + defer func() { + if err := os.RemoveAll(tmpdir2); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() + defer func() { + if err := os.RemoveAll(tmpdir3); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() config1 := types.VMConfig{ Cache: types.CacheOptions{ @@ -510,7 +540,8 @@ func TestGetPinnedMetrics(t *testing.T) { for _, structure := range list { if bytes.Equal(structure.Checksum, checksum) { - found = &structure.Metrics + metrics := structure.Metrics // Create local copy + found = &metrics break } } @@ -784,7 +815,7 @@ func TestExecuteStorageLoop(t *testing.T) { // the "sdk gas" * GasMultiplier + the wasm cost should equal the maxGas (or be very close) totalCost := gasReport.UsedInternally + gasMeter2.GasConsumed() - require.Equal(t, int64(maxGas), int64(totalCost)) + require.Equal(t, maxGas, totalCost) } func BenchmarkContractCall(b *testing.B) { @@ -1230,6 +1261,7 @@ func createFloaty2(tb testing.TB, cache Cache) []byte { func createContract(tb testing.TB, cache Cache, wasmFile string) []byte { tb.Helper() + // #nosec G304 - used for test files only wasm, err := os.ReadFile(wasmFile) require.NoError(tb, err) checksum, err := StoreCode(cache, wasm, true) @@ -1237,7 +1269,7 @@ func createContract(tb testing.TB, cache Cache, wasmFile string) []byte { return checksum } -// exec runs the handle tx with the given signer +// exec runs the handle tx with the given signer. func exec(t *testing.T, cache Cache, checksum []byte, signer types.HumanAddress, store types.KVStore, api *types.GoAPI, querier Querier, gasExpected uint64) types.ContractResult { t.Helper() gasMeter := NewMockGasMeter(TESTING_GAS_LIMIT) @@ -1337,10 +1369,6 @@ func TestCustomReflectQuerier(t *testing.T) { // https://github.com/CosmWasm/cosmwasm/blob/v0.11.0-alpha3/contracts/reflect/src/msg.rs#L18-L28 } - type CapitalizedResponse struct { - Text string `json:"text"` - } - cache, cleanup := withCache(t) defer cleanup() checksum := createReflectContract(t, cache) @@ -1379,7 +1407,11 @@ func TestCustomReflectQuerier(t *testing.T) { require.Equal(t, "SMALL FRYS :)", response.Text) } -// TestFloats is a port of the float_instrs_are_deterministic test in cosmwasm-vm +type CapitalizedResponse struct { + Text string `json:"text"` +} + +// TestFloats is a port of the float_instrs_are_deterministic test in cosmwasm-vm. func TestFloats(t *testing.T) { type Value struct { U32 *uint32 `json:"u32,omitempty"` @@ -1390,15 +1422,16 @@ func TestFloats(t *testing.T) { // helper to print the value in the same format as Rust's Debug trait debugStr := func(value Value) string { - if value.U32 != nil { + switch { + case value.U32 != nil: return fmt.Sprintf("U32(%d)", *value.U32) - } else if value.U64 != nil { + case value.U64 != nil: return fmt.Sprintf("U64(%d)", *value.U64) - } else if value.F32 != nil { + case value.F32 != nil: return fmt.Sprintf("F32(%d)", *value.F32) - } else if value.F64 != nil { + case value.F64 != nil: return fmt.Sprintf("F64(%d)", *value.F64) - } else { + default: t.FailNow() return "" } diff --git a/internal/api/memory.go b/internal/api/memory.go index f2fb06d73..7b426d911 100644 --- a/internal/api/memory.go +++ b/internal/api/memory.go @@ -46,15 +46,16 @@ func uninitializedUnmanagedVector() C.UnmanagedVector { } func newUnmanagedVector(data []byte) C.UnmanagedVector { - if data == nil { + switch { + case data == nil: return C.new_unmanaged_vector(cbool(true), cu8_ptr(nil), cusize(0)) - } else if len(data) == 0 { + case len(data) == 0: // in Go, accessing the 0-th element of an empty array triggers a panic. That is why in the case // of an empty `[]byte` we can't get the internal heap pointer to the underlying array as we do // below with `&data[0]`. // https://play.golang.org/p/xvDY3g9OqUk return C.new_unmanaged_vector(cbool(false), cu8_ptr(nil), cusize(0)) - } else { + default: // This will allocate a proper vector with content and return a description of it return C.new_unmanaged_vector(cbool(false), cu8_ptr(unsafe.Pointer(&data[0])), cusize(len(data))) } @@ -62,14 +63,15 @@ func newUnmanagedVector(data []byte) C.UnmanagedVector { func copyAndDestroyUnmanagedVector(v C.UnmanagedVector) []byte { var out []byte - if v.is_none { + switch { + case bool(v.is_none): out = nil - } else if v.cap == cusize(0) { + case v.cap == cusize(0): // There is no allocation we can copy out = []byte{} - } else { + default: // C.GoBytes create a copy (https://stackoverflow.com/a/40950744/2013738) - out = C.GoBytes(unsafe.Pointer(v.ptr), cint(v.len)) + out = C.GoBytes(unsafe.Pointer(v.ptr), C.int(v.len)) } C.destroy_unmanaged_vector(v) return out @@ -93,6 +95,6 @@ func copyU8Slice(view C.U8SliceView) []byte { return []byte{} } // C.GoBytes create a copy (https://stackoverflow.com/a/40950744/2013738) - res := C.GoBytes(unsafe.Pointer(view.ptr), cint(view.len)) + res := C.GoBytes(unsafe.Pointer(view.ptr), C.int(view.len)) return res } diff --git a/internal/api/memory_test.go b/internal/api/memory_test.go index 397faf50c..749809f9d 100644 --- a/internal/api/memory_test.go +++ b/internal/api/memory_test.go @@ -28,8 +28,8 @@ func TestCreateAndDestroyUnmanagedVector(t *testing.T) { original := []byte{0xaa, 0xbb, 0x64} unmanaged := newUnmanagedVector(original) require.Equal(t, cbool(false), unmanaged.is_none) - require.Equal(t, 3, int(unmanaged.len)) - require.GreaterOrEqual(t, 3, int(unmanaged.cap)) // Rust implementation decides this + require.Equal(t, uint64(3), uint64(unmanaged.len)) + require.GreaterOrEqual(t, uint64(3), uint64(unmanaged.cap)) // Rust implementation decides this copy := copyAndDestroyUnmanagedVector(unmanaged) require.Equal(t, original, copy) } @@ -39,8 +39,8 @@ func TestCreateAndDestroyUnmanagedVector(t *testing.T) { original := []byte{} unmanaged := newUnmanagedVector(original) require.Equal(t, cbool(false), unmanaged.is_none) - require.Equal(t, 0, int(unmanaged.len)) - require.GreaterOrEqual(t, 0, int(unmanaged.cap)) // Rust implementation decides this + require.Equal(t, uint64(0), uint64(unmanaged.len)) + require.GreaterOrEqual(t, uint64(0), uint64(unmanaged.cap)) // Rust implementation decides this copy := copyAndDestroyUnmanagedVector(unmanaged) require.Equal(t, original, copy) } @@ -63,14 +63,16 @@ func TestCreateAndDestroyUnmanagedVector(t *testing.T) { func TestCopyDestroyUnmanagedVector(t *testing.T) { { // ptr, cap and len broken. Do not access those values when is_none is true - invalid_ptr := unsafe.Pointer(uintptr(42)) + base := unsafe.Pointer(&struct{ x byte }{}) //nolint:gosec + invalid_ptr := unsafe.Add(base, 42) uv := constructUnmanagedVector(cbool(true), cu8_ptr(invalid_ptr), cusize(0xBB), cusize(0xAA)) copy := copyAndDestroyUnmanagedVector(uv) require.Nil(t, copy) } { // Capacity is 0, so no allocation happened. Do not access the pointer. - invalid_ptr := unsafe.Pointer(uintptr(42)) + base := unsafe.Pointer(&struct{ x byte }{}) //nolint:gosec + invalid_ptr := unsafe.Add(base, 42) uv := constructUnmanagedVector(cbool(false), cu8_ptr(invalid_ptr), cusize(0), cusize(0)) copy := copyAndDestroyUnmanagedVector(uv) require.Equal(t, []byte{}, copy) diff --git a/internal/api/mocks.go b/internal/api/mocks.go index 225904224..9bec7e1f0 100644 --- a/internal/api/mocks.go +++ b/internal/api/mocks.go @@ -15,6 +15,10 @@ import ( "github.com/CosmWasm/wasmvm/v3/types" ) +const ( + testAddress = "foobar" +) + /** helper constructors **/ const MOCK_CONTRACT_ADDR = "contract" @@ -24,7 +28,7 @@ func MockEnv() types.Env { Block: types.BlockInfo{ Height: 123, Time: 1578939743_987654321, - ChainID: "foobar", + ChainID: testAddress, }, Transaction: &types.TransactionInfo{ Index: 4, @@ -248,7 +252,7 @@ func (g *mockGasMeter) ConsumeGas(amount types.Gas, descriptor string) { // Note: these gas prices are all in *wasmer gas* and (sdk gas * 100) // // We making simple values and non-clear multiples so it is easy to see their impact in test output -// Also note we do not charge for each read on an iterator (out of simplicity and not needed for tests) +// Also note we do not charge for each read on an iterator (out of simplicity and not needed for tests). const ( GetPrice uint64 = 99000 SetPrice uint64 = 187000 @@ -374,7 +378,7 @@ func MockValidateAddress(input string) (gasCost uint64, _ error) { if err != nil { return gasCost, err } - if humanized != strings.ToLower(input) { + if !strings.EqualFold(humanized, input) { return gasCost, fmt.Errorf("address validation failed") } @@ -390,15 +394,14 @@ func NewMockAPI() *types.GoAPI { } func TestMockApi(t *testing.T) { - human := "foobar" - canon, cost, err := MockCanonicalizeAddress(human) + canon, cost, err := MockCanonicalizeAddress(testAddress) require.NoError(t, err) require.Len(t, canon, CanonicalLength) require.Equal(t, CostCanonical, cost) recover, cost, err := MockHumanizeAddress(canon) require.NoError(t, err) - require.Equal(t, recover, human) + require.Equal(t, testAddress, recover) require.Equal(t, CostHuman, cost) } @@ -502,7 +505,7 @@ func (q NoCustom) Query(request json.RawMessage) ([]byte, error) { return nil, types.UnsupportedRequest{Kind: "custom"} } -// ReflectCustom fulfills the requirements for testing `reflect` contract +// ReflectCustom fulfills the requirements for testing `reflect` contract. type ReflectCustom struct{} var _ CustomQuerier = ReflectCustom{} @@ -516,7 +519,7 @@ type CapitalizedQuery struct { Text string `json:"text"` } -// CustomResponse is the response for all `CustomQuery`s +// CustomResponse is the response for all `CustomQuery`s. type CustomResponse struct { Msg string `json:"msg"` } @@ -528,17 +531,18 @@ func (q ReflectCustom) Query(request json.RawMessage) ([]byte, error) { return nil, err } var resp CustomResponse - if query.Ping != nil { + switch { + case query.Ping != nil: resp.Msg = "PONG" - } else if query.Capitalized != nil { + case query.Capitalized != nil: resp.Msg = strings.ToUpper(query.Capitalized.Text) - } else { + default: return nil, errors.New("unsupported query") } return json.Marshal(resp) } -//************ test code for mocks *************************// +// ************ test code for mocks *************************// func TestBankQuerierAllBalances(t *testing.T) { addr := "foobar" diff --git a/internal/api/testdb/memdb.go b/internal/api/testdb/memdb.go index 5e667ced7..212d506ea 100644 --- a/internal/api/testdb/memdb.go +++ b/internal/api/testdb/memdb.go @@ -13,7 +13,7 @@ const ( bTreeDegree = 32 ) -// item is a btree.Item with byte slices as keys and values +// item is a btree.Item with byte slices as keys and values. type item struct { key []byte value []byte diff --git a/lib.go b/lib.go index f9b044116..8133ad71d 100644 --- a/lib.go +++ b/lib.go @@ -14,19 +14,19 @@ import ( // Checksum represents a hash of the Wasm bytecode that serves as an ID. Must be generated from this library. type Checksum = types.Checksum -// WasmCode is an alias for raw bytes of the wasm compiled code +// WasmCode is an alias for raw bytes of the wasm compiled code. type WasmCode []byte -// KVStore is a reference to some sub-kvstore that is valid for one instance of a code +// KVStore is a reference to some sub-kvstore that is valid for one instance of a code. type KVStore = types.KVStore -// GoAPI is a reference to some "precompiles", go callbacks +// GoAPI is a reference to some "precompiles", go callbacks. type GoAPI = types.GoAPI -// Querier lets us make read-only queries on other modules +// Querier lets us make read-only queries on other modules. type Querier = types.Querier -// GasMeter is a read-only version of the sdk gas meter +// GasMeter is a read-only version of the sdk gas meter. type GasMeter = types.GasMeter // LibwasmvmVersion returns the version of the loaded library diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index d2513d961..1956741fa 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -128,7 +128,7 @@ func (vm *VM) Unpin(checksum Checksum) error { // Returns a report of static analysis of the wasm contract (uncompiled). // This contract must have been stored in the cache previously (via Create). -// Only info currently returned is if it exposes all ibc entry points, but this may grow later +// Only info currently returned is if it exposes all ibc entry points, but this may grow later. func (vm *VM) AnalyzeCode(checksum Checksum) (*types.AnalysisReport, error) { return api.AnalyzeCode(vm.cache, checksum) } @@ -190,7 +190,7 @@ func (vm *VM) Instantiate( // (That is a detail for the external, sdk-facing, side). // // The caller is responsible for passing the correct `store` (which must have been initialized exactly once), -// and setting the env with relevant info on this instance (address, balance, etc) +// and setting the env with relevant info on this instance (address, balance, etc). func (vm *VM) Execute( checksum Checksum, env types.Env, @@ -226,7 +226,7 @@ func (vm *VM) Execute( // Query allows a client to execute a contract-specific query. If the result is not empty, it should be // valid json-encoded data to return to the client. -// The meaning of path and data can be determined by the code. Path is the suffix of the abci.QueryRequest.Path +// The meaning of path and data can be determined by the code. Path is the suffix of the abci.QueryRequest.Path. func (vm *VM) Query( checksum Checksum, env types.Env, @@ -370,7 +370,7 @@ func (vm *VM) Sudo( // Reply allows the native Go wasm modules to make a privileged call to return the result // of executing a SubMsg. // -// These work much like Sudo (same scenario) but focuses on one specific case (and one message type) +// These work much like Sudo (same scenario) but focuses on one specific case (and one message type). func (vm *VM) Reply( checksum Checksum, env types.Env, @@ -404,7 +404,7 @@ func (vm *VM) Reply( } // IBCChannelOpen is available on IBC-enabled contracts and is a hook to call into -// during the handshake pahse +// during the handshake pahse. func (vm *VM) IBCChannelOpen( checksum Checksum, env types.Env, @@ -438,7 +438,7 @@ func (vm *VM) IBCChannelOpen( } // IBCChannelConnect is available on IBC-enabled contracts and is a hook to call into -// during the handshake pahse +// during the handshake pahse. func (vm *VM) IBCChannelConnect( checksum Checksum, env types.Env, @@ -472,7 +472,7 @@ func (vm *VM) IBCChannelConnect( } // IBCChannelClose is available on IBC-enabled contracts and is a hook to call into -// at the end of the channel lifetime +// at the end of the channel lifetime. func (vm *VM) IBCChannelClose( checksum Checksum, env types.Env, @@ -506,7 +506,7 @@ func (vm *VM) IBCChannelClose( } // IBCPacketReceive is available on IBC-enabled contracts and is called when an incoming -// packet is received on a channel belonging to this contract +// packet is received on a channel belonging to this contract. func (vm *VM) IBCPacketReceive( checksum Checksum, env types.Env, @@ -541,7 +541,7 @@ func (vm *VM) IBCPacketReceive( // IBCPacketAck is available on IBC-enabled contracts and is called when an // the response for an outgoing packet (previously sent by this contract) -// is received +// is received. func (vm *VM) IBCPacketAck( checksum Checksum, env types.Env, @@ -576,7 +576,7 @@ func (vm *VM) IBCPacketAck( // IBCPacketTimeout is available on IBC-enabled contracts and is called when an // outgoing packet (previously sent by this contract) will provably never be executed. -// Usually handled like ack returning an error +// Usually handled like ack returning an error. func (vm *VM) IBCPacketTimeout( checksum Checksum, env types.Env, @@ -678,8 +678,7 @@ func (vm *VM) IBCDestinationCallback( return &result, gasReport.UsedInternally, nil } -// IBC2PacketReceive is available on IBC-enabled contracts and is called when an incoming -// packet is received on a channel belonging to this contract +// packet is received on a channel belonging to this contract. func (vm *VM) IBC2PacketReceive( checksum Checksum, env types.Env, @@ -762,7 +761,7 @@ type hasSubMessages interface { } // make sure the types implement the interface -// cannot put these next to the types, as the interface is private +// cannot put these next to the types, as the interface is private. var ( _ hasSubMessages = (*types.IBCBasicResult)(nil) _ hasSubMessages = (*types.IBCReceiveResult)(nil) @@ -785,10 +784,11 @@ func DeserializeResponse(gasLimit uint64, deserCost types.UFraction, gasReport * // All responses that have sub-messages need their payload size to be checked const ReplyPayloadMaxBytes = 128 * 1024 // 128 KiB if response, ok := response.(hasSubMessages); ok { - for i, m := range response.SubMessages() { + messages := response.SubMessages() + for i := 0; i < len(messages); i++ { // each payload needs to be below maximum size - if len(m.Payload) > ReplyPayloadMaxBytes { - return fmt.Errorf("reply contains submessage at index %d with payload larger than %d bytes: %d bytes", i, ReplyPayloadMaxBytes, len(m.Payload)) + if len(messages[i].Payload) > ReplyPayloadMaxBytes { + return fmt.Errorf("reply contains submessage at index %d with payload larger than %d bytes: %d bytes", i, ReplyPayloadMaxBytes, len(messages[i].Payload)) } } } diff --git a/lib_libwasmvm_test.go b/lib_libwasmvm_test.go index a4661bbc4..a53740fa0 100644 --- a/lib_libwasmvm_test.go +++ b/lib_libwasmvm_test.go @@ -44,6 +44,7 @@ func withVM(t *testing.T) *VM { func createTestContract(t *testing.T, vm *VM, path string) Checksum { t.Helper() + //#nosec G304 -- This is test code using hardcoded test files wasm, err := os.ReadFile(path) require.NoError(t, err) checksum, _, err := vm.StoreCode(wasm, TESTING_GAS_LIMIT) diff --git a/types/env_test.go b/types/env_test.go index d4ea14ad4..a9d8a837d 100644 --- a/types/env_test.go +++ b/types/env_test.go @@ -20,10 +20,10 @@ func TestMessageInfoHandlesMultipleCoins(t *testing.T) { require.NoError(t, err) // we can unmarshal it properly into struct - var recover MessageInfo - err = json.Unmarshal(bz, &recover) + var recovery MessageInfo + err = json.Unmarshal(bz, &recovery) require.NoError(t, err) - assert.Equal(t, info, recover) + assert.Equal(t, info, recovery) } func TestMessageInfoHandlesMissingCoins(t *testing.T) { diff --git a/types/ibc.go b/types/ibc.go index 8f2715363..e7bfe18db 100644 --- a/types/ibc.go +++ b/types/ibc.go @@ -194,7 +194,7 @@ type IBCDestinationCallbackMsg struct { // Auto-gen code: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/ibc/core/04-channel/types/channel.pb.go#L70-L101 type IBCOrder = string -// These are the only two valid values for IbcOrder +// These are the only two valid values for IbcOrder. const ( Unordered = "ORDER_UNORDERED" Ordered = "ORDER_ORDERED" @@ -203,7 +203,7 @@ const ( // IBCTimeoutBlock Height is a monotonically increasing data type // that can be compared against another Height for the purposes of updating and // freezing clients. -// Ordering is (revision_number, timeout_height) +// Ordering is (revision_number, timeout_height). type IBCTimeoutBlock struct { // the version that the client is currently on // (eg. after resetting the chain this could increment 1 as height drops to 0) @@ -246,7 +246,7 @@ type IBCChannelOpenResult struct { Err string `json:"error,omitempty"` } -// IBC3ChannelOpenResponse is version negotiation data for the handshake +// IBC3ChannelOpenResponse is version negotiation data for the handshake. type IBC3ChannelOpenResponse struct { Version string `json:"version"` } @@ -257,7 +257,7 @@ type IBC3ChannelOpenResponse struct { // // Callbacks that have return values (like ibc_receive_packet) // or that cannot redispatch messages (like ibc_channel_open) -// will use other Response types +// will use other Response types. type IBCBasicResult struct { Ok *IBCBasicResponse `json:"ok,omitempty"` Err string `json:"error,omitempty"` @@ -291,7 +291,7 @@ type IBCBasicResponse struct { // // Callbacks that have return values (like receive_packet) // or that cannot redispatch messages (like the handshake callbacks) -// will use other Response types +// will use other Response types. type IBCReceiveResult struct { Ok *IBCReceiveResponse `json:"ok,omitempty"` Err string `json:"error,omitempty"` diff --git a/types/json_size.go b/types/json_size.go index 014547bc0..6287c6ed0 100644 --- a/types/json_size.go +++ b/types/json_size.go @@ -18,15 +18,16 @@ func ExpectedJSONSizeString(s string) int { // 2x quote + length of string + escaping overhead out := quotes + len(s) for _, r := range s { - if r == '"' || r == '\\' { + switch { + case r == '"' || r == '\\': out += 1 - } else if r == '\b' || r == '\f' || r == '\n' || r == '\r' || r == '\t' { + case r == '\b' || r == '\f' || r == '\n' || r == '\r' || r == '\t': // https://cs.opensource.google/go/go/+/master:src/encoding/json/encode.go;l=992-1001;drc=0909bcd9e4acb01089d588d608d669d69710e50a out += 1 - } else if r <= 0x1F { + case r <= 0x1F: // control codes \u0000 - \u001f out += 5 - } else if r == '<' || r == '>' || r == '&' { + case r == '<' || r == '>' || r == '&': // Go escapes HTML which is a bit pointless but legal // \u003c, \u003e, \u0026 out += 5 @@ -99,7 +100,7 @@ func ExpectedJSONSizeBool(b bool) int { } } -// The size in bytes in JSON serialization +// The size in bytes in JSON serialization. const ( brackets int = 2 // a pair of brackets {} or [] quotes int = 2 // a pair of quotes "" diff --git a/types/msg.go b/types/msg.go index 55ee19ae8..0404b96ac 100644 --- a/types/msg.go +++ b/types/msg.go @@ -43,7 +43,7 @@ type Event struct { Attributes Array[EventAttribute] `json:"attributes"` } -// EventAttribute +// EventAttribute. type EventAttribute struct { Key string `json:"key"` Value string `json:"value"` @@ -110,7 +110,7 @@ type BankMsg struct { } // SendMsg contains instructions for a Cosmos-SDK/SendMsg -// It has a fixed interface here and should be converted into the proper SDK format before dispatching +// It has a fixed interface here and should be converted into the proper SDK format before dispatching. type SendMsg struct { ToAddress string `json:"to_address"` Amount Array[Coin] `json:"amount"` @@ -331,7 +331,7 @@ type WithdrawDelegatorRewardMsg struct { } // FundCommunityPoolMsg is translated to a [MsgFundCommunityPool](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#LL69C1-L76C2). -// `depositor` is automatically filled with the current contract's address +// `depositor` is automatically filled with the current contract's address. type FundCommunityPoolMsg struct { // Amount is the list of coins to be send to the community pool Amount Array[Coin] `json:"amount"` @@ -353,7 +353,7 @@ type WasmMsg struct { ClearAdmin *ClearAdminMsg `json:"clear_admin,omitempty"` } -// These are messages in the IBC lifecycle using the new IBC2 approach. Only usable by IBC2-enabled contracts +// These are messages in the IBC lifecycle using the new IBC2 approach. Only usable by IBC2-enabled contracts. type IBC2Msg struct { SendPacket *IBC2SendPacketMsg `json:"send_packet,omitempty"` WriteAcknowledgement *IBC2WriteAcknowledgementMsg `json:"write_acknowledgement,omitempty"` diff --git a/types/queries.go b/types/queries.go index 11fb37d6d..37cde83f1 100644 --- a/types/queries.go +++ b/types/queries.go @@ -16,11 +16,11 @@ type queryResultImpl struct { } // A custom serializer that allows us to map QueryResult instances to the Rust -// enum `ContractResult` +// enum `ContractResult`. func (q QueryResult) MarshalJSON() ([]byte, error) { // In case both Ok and Err are empty, this is interpreted and serialized // as an Ok case with no data because errors must not be empty. - if len(q.Ok) == 0 && len(q.Err) == 0 { + if len(q.Ok) == 0 && q.Err == "" { return []byte(`{"ok":""}`), nil } return json.Marshal(queryResultImpl(q)) @@ -52,7 +52,7 @@ type Querier interface { GasConsumed() uint64 } -// this is a thin wrapper around the desired Go API to give us types closer to Rust FFI +// this is a thin wrapper around the desired Go API to give us types closer to Rust FFI. func RustQuery(querier Querier, binRequest []byte, gasLimit uint64) QuerierResult { var request QueryRequest err := json.Unmarshal(binRequest, &request) @@ -70,7 +70,7 @@ func RustQuery(querier Querier, binRequest []byte, gasLimit uint64) QuerierResul return ToQuerierResult(bz, err) } -// This is a 2-level result +// This is a 2-level result. type QuerierResult struct { Ok *QueryResult `json:"ok,omitempty"` Err *SystemError `json:"error,omitempty"` @@ -122,7 +122,7 @@ type SupplyQuery struct { Denom string `json:"denom"` } -// SupplyResponse is the expected response to SupplyQuery +// SupplyResponse is the expected response to SupplyQuery. type SupplyResponse struct { Amount Coin `json:"amount"` } @@ -132,7 +132,7 @@ type BalanceQuery struct { Denom string `json:"denom"` } -// BalanceResponse is the expected response to BalanceQuery +// BalanceResponse is the expected response to BalanceQuery. type BalanceResponse struct { Amount Coin `json:"amount"` } @@ -141,7 +141,7 @@ type AllBalancesQuery struct { Address string `json:"address"` } -// AllBalancesResponse is the expected response to AllBalancesQuery +// AllBalancesResponse is the expected response to AllBalancesQuery. type AllBalancesResponse struct { Amount Array[Coin] `json:"amount"` } @@ -226,7 +226,7 @@ type StakingQuery struct { type AllValidatorsQuery struct{} -// AllValidatorsResponse is the expected response to AllValidatorsQuery +// AllValidatorsResponse is the expected response to AllValidatorsQuery. type AllValidatorsResponse struct { Validators Array[Validator] `json:"validators"` } @@ -236,7 +236,7 @@ type ValidatorQuery struct { Address string `json:"address"` } -// ValidatorResponse is the expected response to ValidatorQuery +// ValidatorResponse is the expected response to ValidatorQuery. type ValidatorResponse struct { Validator *Validator `json:"validator"` // serializes to `null` when unset which matches Rust's Option::None serialization } @@ -260,7 +260,7 @@ type DelegationQuery struct { Validator string `json:"validator"` } -// AllDelegationsResponse is the expected response to AllDelegationsQuery +// AllDelegationsResponse is the expected response to AllDelegationsQuery. type AllDelegationsResponse struct { Delegations Array[Delegation] `json:"delegations"` } @@ -324,7 +324,7 @@ type DelegatorValidatorsResponse struct { Validators []string `json:"validators"` } -// DelegationResponse is the expected response to Array[Delegation]Query +// DelegationResponse is the expected response to Array[Delegation]Query. type DelegationResponse struct { Delegation *FullDelegation `json:"delegation,omitempty"` } @@ -375,14 +375,14 @@ type WasmQuery struct { CodeInfo *CodeInfoQuery `json:"code_info,omitempty"` } -// SmartQuery response is raw bytes ([]byte) +// SmartQuery response is raw bytes ([]byte). type SmartQuery struct { // Bech32 encoded sdk.AccAddress of the contract ContractAddr string `json:"contract_addr"` Msg []byte `json:"msg"` } -// RawQuery response is raw bytes ([]byte) +// RawQuery response is raw bytes ([]byte). type RawQuery struct { // Bech32 encoded sdk.AccAddress of the contract ContractAddr string `json:"contract_addr"` diff --git a/types/submessages.go b/types/submessages.go index 6e781db45..e185b6f45 100644 --- a/types/submessages.go +++ b/types/submessages.go @@ -53,7 +53,7 @@ func (s *replyOn) UnmarshalJSON(b []byte) error { } // SubMsg wraps a CosmosMsg with some metadata for handling replies (ID) and optionally -// limiting the gas usage (GasLimit) +// limiting the gas usage (GasLimit). type SubMsg struct { // An arbitrary ID chosen by the contract. // This is typically used to match `Reply`s in the `reply` entry point to the submessage. diff --git a/types/systemerror.go b/types/systemerror.go index ed0327668..e75d02b40 100644 --- a/types/systemerror.go +++ b/types/systemerror.go @@ -139,7 +139,7 @@ func ToSystemError(err error) *SystemError { } } -// check if an interface is nil (even if it has type info) +// check if an interface is nil (even if it has type info). func isNil(i any) bool { if i == nil { return true diff --git a/types/types.go b/types/types.go index 5699367ff..d6f79399b 100644 --- a/types/types.go +++ b/types/types.go @@ -8,7 +8,7 @@ import ( "github.com/shamaton/msgpack/v2" ) -// Uint64 is a wrapper for uint64, but it is marshalled to and from JSON as a string +// Uint64 is a wrapper for uint64, but it is marshalled to and from JSON as a string. type Uint64 uint64 func (u Uint64) MarshalJSON() ([]byte, error) { @@ -28,7 +28,7 @@ func (u *Uint64) UnmarshalJSON(data []byte) error { return nil } -// Int64 is a wrapper for int64, but it is marshalled to and from JSON as a string +// Int64 is a wrapper for int64, but it is marshalled to and from JSON as a string. type Int64 int64 func (i Int64) MarshalJSON() ([]byte, error) { @@ -51,10 +51,10 @@ func (i *Int64) UnmarshalJSON(data []byte) error { // HumanAddress is a printable (typically bech32 encoded) address string. Just use it as a label for developers. type HumanAddress = string -// CanonicalAddress uses standard base64 encoding, just use it as a label for developers +// CanonicalAddress uses standard base64 encoding, just use it as a label for developers. type CanonicalAddress = []byte -// Coin is a string representation of the sdk.Coin type (more portable than sdk.Int) +// Coin is a string representation of the sdk.Coin type (more portable than sdk.Int). type Coin struct { // Denom is the denomination string registered in the chain's bank module. // E.g. "uatom" or "ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2". @@ -73,7 +73,7 @@ func NewCoin(amount uint64, denom string) Coin { } } -// Replicating the cosmos-sdk bank module Metadata type +// Replicating the cosmos-sdk bank module Metadata type. type DenomMetadata struct { Description string `json:"description"` // DenomUnits represents the list of DenomUnits for a given coin @@ -103,7 +103,7 @@ type DenomMetadata struct { URIHash string `json:"uri_hash"` } -// Replicating the cosmos-sdk bank module DenomUnit type +// Replicating the cosmos-sdk bank module DenomUnit type. type DenomUnit struct { // Denom represents the string name of the given denom unit (e.g uatom). Denom string `json:"denom"` @@ -134,7 +134,7 @@ type DecCoin struct { Denom string `json:"denom"` } -// Simplified version of the cosmos-sdk PageRequest type +// Simplified version of the cosmos-sdk PageRequest type. type PageRequest struct { // Key is a value returned in PageResponse.next_key to begin // querying the next page most efficiently. Only one of offset or key @@ -240,7 +240,7 @@ type MigrateInfo struct { OldMigrateVersion *uint64 `json:"old_migrate_version"` } -// MarshalJSON ensures that we get "[]" for nil arrays +// MarshalJSON ensures that we get "[]" for nil arrays. func (a Array[C]) MarshalJSON() ([]byte, error) { if len(a) == 0 { return []byte("[]"), nil @@ -249,7 +249,7 @@ func (a Array[C]) MarshalJSON() ([]byte, error) { return json.Marshal(raw) } -// UnmarshalJSON ensures that we get an empty slice for "[]" and "null" +// UnmarshalJSON ensures that we get an empty slice for "[]" and "null". func (a *Array[C]) UnmarshalJSON(data []byte) error { var raw []C if err := json.Unmarshal(data, &raw); err != nil {