From fdaf809691ea6417e46d484453083de44ac75db5 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 22 Dec 2024 20:03:38 +0700 Subject: [PATCH 01/26] enable additional linters --- .github/workflows/lint-go.yml | 2 +- .golangci.yml | 47 +++++++++++++++++++++++++++++++---- cmd/demo/main.go | 2 +- go.mod | 2 +- ibc_test.go | 2 +- internal/api/iterator.go | 6 ++--- internal/api/lib_test.go | 8 +++--- internal/api/mocks.go | 8 +++--- internal/api/testdb/memdb.go | 2 +- lib.go | 10 ++++---- lib_libwasmvm.go | 22 ++++++++-------- types/ibc.go | 10 ++++---- types/msg.go | 6 ++--- types/queries.go | 24 +++++++++--------- types/submessages.go | 2 +- types/systemerror.go | 2 +- types/types.go | 18 +++++++------- 17 files changed, 105 insertions(+), 68 deletions(-) diff --git a/.github/workflows/lint-go.yml b/.github/workflows/lint-go.yml index e901160ec..3893e0165 100644 --- a/.github/workflows/lint-go.yml +++ b/.github/workflows/lint-go.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: "1.21.4" + go-version: "1.23" cache: false - name: golangci-lint uses: golangci/golangci-lint-action@v3.6.0 diff --git a/.golangci.yml b/.golangci.yml index fd168cf9a..132c69402 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,15 +1,36 @@ run: tests: true + timeout: 5m + skip-dirs: + - vendor/ + - third_party/ linters: # Enable specific linter # https://golangci-lint.run/usage/linters/#enabled-by-default enable: - - gofumpt - - gci - - testifylint - - errcheck - - thelper + - errcheck # Detect unchecked errors + - gosimple # Simplify code + - govet # Reports suspicious constructs + - ineffassign # Detect unused assignments + - staticcheck # Go static analysis + - typecheck # Go type checker + - unused # Detect unused constants, variables, functions and types + + # Additional recommended linters + - gocritic # A more opinionated linter + - gosec # Security checker + - misspell # Find commonly misspelled words + - revive # a metalinter with more checks + - bodyclose # Check HTTP response bodies are closed + - dupl # Code clone detector + - goconst # Find repeated strings that could be constants + - gocyclo # Check function complexity + - godot # Check comment endings + - gocognit # Check cognitive complexity + - whitespace # Check trailing whitespace + - thelper # Detect test helpers not using t.Helper() + - tparallel # Detect incorrect usage of t.Parallel() linters-settings: gci: @@ -37,7 +58,23 @@ linters-settings: # Drops lexical ordering for custom sections. # Default: false no-lex-order: true + gocyclo: + min-complexity: 15 + gocognit: + min-complexity: 20 + dupl: + threshold: 100 + goconst: + min-len: 3 + min-occurrences: 3 + revive: + rules: + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming + - name: var-naming + severity: warning + disabled: true issues: + exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 0 diff --git a/cmd/demo/main.go b/cmd/demo/main.go index 58286e782..8afbfe650 100644 --- a/cmd/demo/main.go +++ b/cmd/demo/main.go @@ -16,7 +16,7 @@ const ( var SUPPORTED_CAPABILITIES = []string{"staking"} -// This is just a demo to ensure we can compile a static go binary +// This is just a demo to ensure we can compile a static go binary. func main() { file := os.Args[1] diff --git a/go.mod b/go.mod index b8a003356..16083455d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/CosmWasm/wasmvm/v2 -go 1.21 +go 1.23 require ( github.com/google/btree v1.0.0 diff --git a/ibc_test.go b/ibc_test.go index 4992ee50b..f47b5cc75 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/iterator.go b/internal/api/iterator.go index c9a768b40..0b3c9cf7b 100644 --- a/internal/api/iterator.go +++ b/internal/api/iterator.go @@ -8,7 +8,7 @@ import ( "github.com/CosmWasm/wasmvm/v2/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 +17,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,7 +44,7 @@ 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) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index ed760c9fc..7e2e48868 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -26,7 +26,7 @@ const ( TESTING_CACHE_SIZE = 100 // MiB ) -// Add mutex for thread safety +// Add mutex for thread safety. var testMutex sync.Mutex var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3"} @@ -1232,7 +1232,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) @@ -1374,7 +1374,7 @@ type CapitalizedResponse struct { Text string `json:"text"` } -// TestFloats is a port of the float_instrs_are_deterministic test in cosmwasm-vm +// 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"` @@ -1471,7 +1471,7 @@ func TestFloats(t *testing.T) { require.Equal(t, "95f70fa6451176ab04a9594417a047a1e4d8e2ff809609b8f81099496bee2393", hex.EncodeToString(hash)) } -// mockInfoBinNoAssert creates the message binary without using testify assertions +// mockInfoBinNoAssert creates the message binary without using testify assertions. func mockInfoBinNoAssert(sender types.HumanAddress) []byte { info := types.MessageInfo{ Sender: sender, diff --git a/internal/api/mocks.go b/internal/api/mocks.go index 7f8547308..b8a249ac0 100644 --- a/internal/api/mocks.go +++ b/internal/api/mocks.go @@ -248,7 +248,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 @@ -502,7 +502,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 +516,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"` } @@ -538,7 +538,7 @@ func (q ReflectCustom) Query(request json.RawMessage) ([]byte, error) { 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 458af0740..a9b90d946 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 3f66b71ea..ad300f558 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, @@ -693,7 +693,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) diff --git a/types/ibc.go b/types/ibc.go index 0b9df4956..6ff29a5a2 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/msg.go b/types/msg.go index 60a6e8751..677f96ab4 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"` @@ -107,7 +107,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"` @@ -328,7 +328,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"` diff --git a/types/queries.go b/types/queries.go index 42a8aa853..80c1b9117 100644 --- a/types/queries.go +++ b/types/queries.go @@ -16,7 +16,7 @@ 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. @@ -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 b0cb81747..ce74f4dca 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 c7ca32029..33cca47a3 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 interface{}) bool { if i == nil { return true diff --git a/types/types.go b/types/types.go index df7582eb4..5df368fd9 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 string `json:"denom"` // type, eg. "ATOM" Amount string `json:"amount"` // string encoding of decimal value, eg. "12.3456" @@ -67,7 +67,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 @@ -97,7 +97,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"` @@ -128,7 +128,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 @@ -233,7 +233,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 @@ -242,7 +242,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 { From 71b2d1eeec72c11441b3c0eff1b21cad07a69668 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 23 Dec 2024 02:16:05 +0700 Subject: [PATCH 02/26] adjust golang version in circleci --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d376d482c..6e1b0c6f5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -186,7 +186,7 @@ jobs: # Build types and cosmwam package without cgo wasmvm_no_cgo: docker: - - image: cimg/go:1.21.4 + - image: cimg/go:1.23.4 steps: - checkout - run: From f3713991f58ea935c0e10e7b2e5596155c067b96 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 23 Dec 2024 02:18:47 +0700 Subject: [PATCH 03/26] bump go... --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6e1b0c6f5..df5af5842 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -168,7 +168,7 @@ jobs: format-go: docker: - - image: cimg/go:1.21.4 + - image: cimg/go:1.23.4 steps: - run: name: Install gofumpt @@ -205,7 +205,7 @@ jobs: # Build types and cosmwasm with libwasmvm linking disabled nolink_libwasmvm: docker: - - image: cimg/go:1.21.4 + - image: cimg/go:1.23.4 steps: - checkout - run: @@ -223,7 +223,7 @@ jobs: tidy-go: docker: - - image: cimg/go:1.21.4 + - image: cimg/go:1.23.4 steps: - checkout - run: @@ -241,7 +241,7 @@ jobs: format-scripts: docker: - - image: cimg/go:1.21.4 + - image: cimg/go:1.23.4 steps: - run: name: Install shfmt @@ -298,7 +298,7 @@ jobs: # Test the Go project and run benchmarks wasmvm_test: docker: - - image: cimg/go:1.21.4 + - image: cimg/go:1.23.4 environment: GORACE: "halt_on_error=1" BUILD_VERSION: $(echo ${CIRCLE_SHA1} | cut -c 1-10) From cbde7172c6ce2ae8131cd20851adfaa11a17e504 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 23 Dec 2024 02:21:19 +0700 Subject: [PATCH 04/26] remove dupl --- .golangci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 132c69402..139768e7a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -23,7 +23,6 @@ linters: - misspell # Find commonly misspelled words - revive # a metalinter with more checks - bodyclose # Check HTTP response bodies are closed - - dupl # Code clone detector - goconst # Find repeated strings that could be constants - gocyclo # Check function complexity - godot # Check comment endings From 08ea4c474357b746b15a60d442592b6a4d1eceb9 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 3 Jan 2025 01:20:19 +0700 Subject: [PATCH 05/26] lint main.go --- .golangci.yml | 2 +- cmd/demo/main.go | 66 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 139768e7a..18cc6fd4b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,7 @@ linters: - revive # a metalinter with more checks - bodyclose # Check HTTP response bodies are closed - goconst # Find repeated strings that could be constants - - gocyclo # Check function complexity +# - gocyclo # Check function complexity - godot # Check comment endings - gocognit # Check cognitive complexity - whitespace # Check trailing whitespace diff --git a/cmd/demo/main.go b/cmd/demo/main.go index 8afbfe650..d54e0a94a 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/v2" ) +// 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") } From 7a812f550e177b75ae2846194254db2753b7753d Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 3 Jan 2025 01:44:09 +0700 Subject: [PATCH 06/26] lints... --- cmd/demo/main.go | 12 ++++---- internal/api/callbacks.go | 22 +++++--------- internal/api/iterator.go | 7 ++++- internal/api/lib.go | 8 ++--- internal/api/lib_test.go | 60 ++++++++++++++++++++++++++++--------- internal/api/memory.go | 22 +++++++------- internal/api/memory_test.go | 14 +++++---- internal/api/mocks.go | 18 ++++++----- 8 files changed, 101 insertions(+), 62 deletions(-) diff --git a/cmd/demo/main.go b/cmd/demo/main.go index d54e0a94a..4d05413d0 100644 --- a/cmd/demo/main.go +++ b/cmd/demo/main.go @@ -10,22 +10,22 @@ import ( wasmvm "github.com/CosmWasm/wasmvm/v2" ) -// PrintDebug enables debug printing when true +// PrintDebug enables debug printing when true. const ( PrintDebug = true - // MemoryLimit defines the memory limit in MiB + // MemoryLimit defines the memory limit in MiB. MemoryLimit = 32 - // CacheSize defines the cache size in MiB + // CacheSize defines the cache size in MiB. CacheSize = 100 ) -// SupportedCapabilities defines the list of supported staking capabilities +// SupportedCapabilities defines the list of supported staking capabilities. var SupportedCapabilities = []string{"staking"} -// exitCode tracks the code that the program will exit with +// 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 +// main is the entry point for the demo application that tests wasmvm functionality. func main() { defer func() { os.Exit(exitCode) diff --git a/internal/api/callbacks.go b/internal/api/callbacks.go index 702c8faf7..043898383 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.") } @@ -272,19 +272,13 @@ func cScan(ptr *C.db_t, gasMeter *C.gas_meter_t, usedGas *cu64, start C.U8SliceV //export cNext func cNext(ref C.IteratorReference, gasMeter *C.gas_meter_t, usedGas *cu64, key *C.UnmanagedVector, val *C.UnmanagedVector, errOut *C.UnmanagedVector) (ret C.GoError) { - // typical usage of iterator - // for ; itr.Valid(); itr.Next() { - // 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 +330,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.") } @@ -384,7 +378,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.") } @@ -412,7 +406,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 +433,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 +473,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 0b3c9cf7b..74539c2a0 100644 --- a/internal/api/iterator.go +++ b/internal/api/iterator.go @@ -3,6 +3,7 @@ package api import ( "fmt" "math" + "os" "sync" "github.com/CosmWasm/wasmvm/v2/types" @@ -50,7 +51,11 @@ func endCall(callID uint64) { 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 2b71adc1c..8f0f7c811 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,7 @@ func InitCache(config types.VMConfig) (Cache, error) { func ReleaseCache(cache Cache) { C.release_cache(cache.ptr) - cache.lockfile.Close() // Also releases the file lock + cache.lockfile.Close() // Also releases the file lock. } func StoreCode(cache Cache, wasm []byte, persist bool) ([]byte, error) { @@ -331,7 +331,7 @@ func Migrate( errmsg := uninitializedUnmanagedVector() res, err := C.migrate(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + if err != nil && err.(syscall.Errno) != 0 { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } @@ -376,7 +376,7 @@ func MigrateWithInfo( errmsg := uninitializedUnmanagedVector() res, err := C.migrate_with_info(cache.ptr, cs, e, m, i, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + if err != nil && err.(syscall.Errno) != 0 { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index e2dd1e8c9..ae129b545 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -34,7 +34,11 @@ var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator", "cosmwasm func TestInitAndReleaseCache(t *testing.T) { tmpdir, err := os.MkdirTemp("", "wasmvm-testing") require.NoError(t, err) - defer os.RemoveAll(tmpdir) + defer func() { + if err := os.RemoveAll(tmpdir); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() config := types.VMConfig{ Cache: types.CacheOptions{ @@ -54,7 +58,11 @@ func TestInitAndReleaseCache(t *testing.T) { func TestInitCacheWorksForNonExistentDir(t *testing.T) { tmpdir, err := os.MkdirTemp("", "wasmvm-testing") require.NoError(t, err) - defer os.RemoveAll(tmpdir) + 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{ @@ -90,7 +98,11 @@ func TestInitCacheErrorsForBrokenDir(t *testing.T) { func TestInitLockingPreventsConcurrentAccess(t *testing.T) { tmpdir, err := os.MkdirTemp("", "wasmvm-testing") require.NoError(t, err) - defer os.RemoveAll(tmpdir) + defer func() { + if err := os.RemoveAll(tmpdir); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() config1 := types.VMConfig{ Cache: types.CacheOptions{ @@ -137,9 +149,21 @@ func TestInitLockingAllowsMultipleInstancesInDifferentDirs(t *testing.T) { require.NoError(t, err) tmpdir3, err := os.MkdirTemp("", "wasmvm-testing3") require.NoError(t, err) - defer os.RemoveAll(tmpdir1) - defer os.RemoveAll(tmpdir2) - defer os.RemoveAll(tmpdir3) + 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{ @@ -180,7 +204,11 @@ func TestInitLockingAllowsMultipleInstancesInDifferentDirs(t *testing.T) { func TestInitCacheEmptyCapabilities(t *testing.T) { tmpdir, err := os.MkdirTemp("", "wasmvm-testing") require.NoError(t, err) - defer os.RemoveAll(tmpdir) + defer func() { + if err := os.RemoveAll(tmpdir); err != nil { + t.Errorf("failed to remove temp dir: %v", err) + } + }() config := types.VMConfig{ Cache: types.CacheOptions{ BaseDir: tmpdir, @@ -210,7 +238,9 @@ func withCache(tb testing.TB) (Cache, func()) { require.NoError(tb, err) cleanup := func() { - os.RemoveAll(tmpdir) + if err := os.RemoveAll(tmpdir); err != nil { + tb.Errorf("failed to remove temp dir: %v", err) + } ReleaseCache(cache) } return cache, cleanup @@ -781,7 +811,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, uint64(maxGas), totalCost) } func BenchmarkContractCall(b *testing.B) { @@ -1225,6 +1255,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) @@ -1385,15 +1416,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..953296fae 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 { - // C.GoBytes create a copy (https://stackoverflow.com/a/40950744/2013738) - out = C.GoBytes(unsafe.Pointer(v.ptr), cint(v.len)) + default: + // C.GoBytes accepts C types directly, avoiding unsafe integer conversions + out = C.GoBytes(unsafe.Pointer(v.ptr), C.int(v.len)) } C.destroy_unmanaged_vector(v) return out @@ -92,7 +94,7 @@ func copyU8Slice(view C.U8SliceView) []byte { // In this case, we don't want to look into the ptr return []byte{} } - // C.GoBytes create a copy (https://stackoverflow.com/a/40950744/2013738) - res := C.GoBytes(unsafe.Pointer(view.ptr), cint(view.len)) + // C.GoBytes accepts C types directly, avoiding unsafe integer conversions + 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 b8a249ac0..4ed17c727 100644 --- a/internal/api/mocks.go +++ b/internal/api/mocks.go @@ -15,6 +15,10 @@ import ( "github.com/CosmWasm/wasmvm/v2/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, @@ -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, recover, testAddress) require.Equal(t, CostHuman, cost) } @@ -528,11 +531,12 @@ 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) From 65039a53378f2aae139e08c25b60656e947d0194 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 3 Jan 2025 01:50:17 +0700 Subject: [PATCH 07/26] complete --- .golangci.yml | 6 +++++- internal/api/lib.go | 5 ++++- lib_libwasmvm_test.go | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 18cc6fd4b..a90d1a3f7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,7 @@ linters: - revive # a metalinter with more checks - bodyclose # Check HTTP response bodies are closed - goconst # Find repeated strings that could be constants -# - gocyclo # Check function complexity + # - gocyclo # Check function complexity - godot # Check comment endings - gocognit # Check cognitive complexity - whitespace # Check trailing whitespace @@ -57,6 +57,10 @@ linters-settings: # Drops lexical ordering for custom sections. # Default: false no-lex-order: true + gocritic: + # Enable all gocritic checks. + disabled-checks: + - dupSubExpr gocyclo: min-complexity: 15 gocognit: diff --git a/internal/api/lib.go b/internal/api/lib.go index 8f0f7c811..3531f8164 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -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/lib_libwasmvm_test.go b/lib_libwasmvm_test.go index 344ce614f..c4c77c728 100644 --- a/lib_libwasmvm_test.go +++ b/lib_libwasmvm_test.go @@ -39,13 +39,16 @@ func withVM(t *testing.T) *VM { t.Cleanup(func() { vm.Cleanup() - os.RemoveAll(tmpdir) + if err := os.RemoveAll(tmpdir); err != nil { + t.Fatal(err) + } }) return 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) From a06cd0d2621cd97b7159a44af37dac55c9d383cc Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 17 Jan 2025 18:13:15 +0700 Subject: [PATCH 08/26] remove mutext lock in lib_test.go --- internal/api/lib_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index ae129b545..be982f60f 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -840,9 +840,7 @@ func BenchmarkContractCall(b *testing.B) { gasMeter2 := NewMockGasMeter(TESTING_GAS_LIMIT) igasMeter2 := types.GasMeter(gasMeter2) store.SetGasMeter(gasMeter2) - testMutex.Lock() info = MockInfoBin(b, "fred") - testMutex.Unlock() msg := []byte(`{"allocate_large_memory":{"pages":0}}`) // replace with noop once we have it res, _, err = Execute(cache, checksum, env, info, msg, &igasMeter2, store, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG) require.NoError(b, err) From 1118f26c7322b988877782629545ed8c4df3c288 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 17 Jan 2025 18:14:57 +0700 Subject: [PATCH 09/26] remove unused variable --- internal/api/lib_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index be982f60f..0e8854cf7 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -26,9 +26,6 @@ const ( TESTING_CACHE_SIZE = 100 // MiB ) -// Add mutex for thread safety. -var testMutex sync.Mutex - var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3"} func TestInitAndReleaseCache(t *testing.T) { From d6c14e665f7c729482f7741911f60a1530f08c7c Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 17 Jan 2025 18:26:50 +0700 Subject: [PATCH 10/26] restore comments --- internal/api/memory.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/api/memory.go b/internal/api/memory.go index 953296fae..7b426d911 100644 --- a/internal/api/memory.go +++ b/internal/api/memory.go @@ -70,7 +70,7 @@ func copyAndDestroyUnmanagedVector(v C.UnmanagedVector) []byte { // There is no allocation we can copy out = []byte{} default: - // C.GoBytes accepts C types directly, avoiding unsafe integer conversions + // C.GoBytes create a copy (https://stackoverflow.com/a/40950744/2013738) out = C.GoBytes(unsafe.Pointer(v.ptr), C.int(v.len)) } C.destroy_unmanaged_vector(v) @@ -94,7 +94,7 @@ func copyU8Slice(view C.U8SliceView) []byte { // In this case, we don't want to look into the ptr return []byte{} } - // C.GoBytes accepts C types directly, avoiding unsafe integer conversions + // C.GoBytes create a copy (https://stackoverflow.com/a/40950744/2013738) res := C.GoBytes(unsafe.Pointer(view.ptr), C.int(view.len)) return res } From d9438025f429d9ca7d081e67328996f4360089d2 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 17 Jan 2025 18:30:24 +0700 Subject: [PATCH 11/26] fix lint in .golangci.yml --- .golangci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index a90d1a3f7..895be36c7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,14 +1,12 @@ run: tests: true timeout: 5m - skip-dirs: - - vendor/ - - third_party/ linters: # Enable specific linter # https://golangci-lint.run/usage/linters/#enabled-by-default enable: + - copyloopvar # Detect copy loops - errcheck # Detect unchecked errors - gosimple # Simplify code - govet # Reports suspicious constructs @@ -81,3 +79,6 @@ issues: exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 0 + exclude-dirs: + - vendor/ + - third_party/ From ce052d68416a352b9fb8be2af0756e92d2a51ba4 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sat, 1 Feb 2025 10:26:41 +0700 Subject: [PATCH 12/26] lint --- types/msg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/msg.go b/types/msg.go index 394a67dd9..d622bcd65 100644 --- a/types/msg.go +++ b/types/msg.go @@ -353,7 +353,7 @@ type WasmMsg struct { ClearAdmin *ClearAdminMsg `json:"clear_admin,omitempty"` } -// These are messages in the IBC lifecycle using the new Eureka approach. Only usable by IBC-enabled contracts +// These are messages in the IBC lifecycle using the new Eureka approach. Only usable by IBC-enabled contracts. type EurekaMsg struct { SendPacket *EurekaSendPacketMsg `json:"send_packet,omitempty"` } From 59fa5a4424a6b5422a71eb95ed4d4330f45b23d0 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 14 Feb 2025 22:26:33 +0700 Subject: [PATCH 13/26] Refactor test helper function for creating message binary --- internal/api/lib_test.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index 1972f4a72..e2627d6fb 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -899,7 +899,7 @@ func Benchmark100ConcurrentContractCalls(b *testing.B) { resChan := make(chan []byte, callCount) wg.Add(callCount) - info = mockInfoBinNoAssert("fred") + info = MockInfoBin(b, "fred") for i := 0; i < callCount; i++ { go func() { @@ -1527,16 +1527,3 @@ func TestFloats(t *testing.T) { hash := hasher.Sum(nil) require.Equal(t, "95f70fa6451176ab04a9594417a047a1e4d8e2ff809609b8f81099496bee2393", hex.EncodeToString(hash)) } - -// mockInfoBinNoAssert creates the message binary without using testify assertions. -func mockInfoBinNoAssert(sender types.HumanAddress) []byte { - info := types.MessageInfo{ - Sender: sender, - Funds: types.Array[types.Coin]{}, - } - res, err := json.Marshal(info) - if err != nil { - panic(err) - } - return res -} From 20aaea0519dbeb2c7a2e77f5fd11ed59512b4c24 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Tue, 18 Feb 2025 20:17:14 +0700 Subject: [PATCH 14/26] revert unnecessary change --- internal/api/lib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/lib.go b/internal/api/lib.go index 45802bbd2..4ecf532fe 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -334,7 +334,7 @@ func Migrate( errmsg := uninitializedUnmanagedVector() res, err := C.migrate(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) - if err != nil && err.(syscall.Errno) != 0 { + if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } From 19d75a5209cc5b69bb2fd956a4194c9b2013447f Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Wed, 19 Feb 2025 22:16:17 +0700 Subject: [PATCH 15/26] resolve merge conflict and reduce required go version --- .github/workflows/lint-go.yml | 5 ----- go.mod | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/lint-go.yml b/.github/workflows/lint-go.yml index ba7bd6c4d..231c5485e 100644 --- a/.github/workflows/lint-go.yml +++ b/.github/workflows/lint-go.yml @@ -20,11 +20,6 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: -<<<<<<< HEAD - - -======= ->>>>>>> faddat/errcheck go-version: "1.23.4" cache: false - name: golangci-lint diff --git a/go.mod b/go.mod index 16083455d..b8a003356 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/CosmWasm/wasmvm/v2 -go 1.23 +go 1.21 require ( github.com/google/btree v1.0.0 From f74e2595bd63528f4a2d43b92cdf303b68f348e1 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Wed, 19 Feb 2025 22:20:15 +0700 Subject: [PATCH 16/26] adjust loop to go 1.21 --- internal/api/lib_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index e2627d6fb..ee8478e5e 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -556,7 +556,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 } } From 66ff709d7e32a2fe933ad52729f272795f3cebb5 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 25 Apr 2025 09:36:37 +0700 Subject: [PATCH 17/26] all linters excecpt revive --- .golangci.yml | 8 ++++---- internal/api/mocks.go | 2 +- lib_libwasmvm.go | 3 +-- types/json_size.go | 2 +- types/msg.go | 2 +- types/systemerror.go | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 344bf2e53..bb264aa86 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,26 +13,26 @@ linters: - thelper - copyloopvar # Detect copy loops - errcheck # Detect unchecked errors - - gosimple # Simplify code - govet # Reports suspicious constructs - ineffassign # Detect unused assignments - staticcheck # Go static analysis - - typecheck # Go type checker - unused # Detect unused constants, variables, functions and types # Additional recommended linters - gocritic # A more opinionated linter - gosec # Security checker - misspell # Find commonly misspelled words - - revive # a metalinter with more checks - bodyclose # Check HTTP response bodies are closed - goconst # Find repeated strings that could be constants - # - gocyclo # Check function complexity - godot # Check comment endings - 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 + exclusions: generated: lax presets: diff --git a/internal/api/mocks.go b/internal/api/mocks.go index 4ed17c727..978c28e7e 100644 --- a/internal/api/mocks.go +++ b/internal/api/mocks.go @@ -401,7 +401,7 @@ func TestMockApi(t *testing.T) { recover, cost, err := MockHumanizeAddress(canon) require.NoError(t, err) - require.Equal(t, recover, testAddress) + require.Equal(t, testAddress, recover) require.Equal(t, CostHuman, cost) } diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index 19dfd579b..dcda5718d 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -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, diff --git a/types/json_size.go b/types/json_size.go index 014547bc0..cfd9775eb 100644 --- a/types/json_size.go +++ b/types/json_size.go @@ -99,7 +99,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 225aabca8..0404b96ac 100644 --- a/types/msg.go +++ b/types/msg.go @@ -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/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 From e7e28b87f5dd26006a456244075d2da8586035cf Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 25 Apr 2025 09:51:54 +0700 Subject: [PATCH 18/26] gocritic --- .golangci.yml | 10 +++++++++- internal/api/callbacks.go | 4 ++-- internal/api/mocks.go | 4 ++-- lib_libwasmvm.go | 7 ++++--- types/env_test.go | 6 +++--- types/json_size.go | 9 +++++---- types/queries.go | 2 +- 7 files changed, 26 insertions(+), 16 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index bb264aa86..4ff99be7d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,6 @@ linters: - misspell # Find commonly misspelled words - bodyclose # Check HTTP response bodies are closed - goconst # Find repeated strings that could be constants - - godot # Check comment endings - gocognit # Check cognitive complexity - whitespace # Check trailing whitespace - thelper # Detect test helpers not using t.Helper() @@ -33,6 +32,15 @@ linters: # enable lager because changes are huge # - revive + settings: + gocritic: + enable-all: true + disabled-checks: + - dupSubExpr + - dupImport + + + exclusions: generated: lax presets: diff --git a/internal/api/callbacks.go b/internal/api/callbacks.go index 043898383..0bb1497d0 100644 --- a/internal/api/callbacks.go +++ b/internal/api/callbacks.go @@ -350,7 +350,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 @@ -392,7 +392,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)) diff --git a/internal/api/mocks.go b/internal/api/mocks.go index 978c28e7e..c65e60561 100644 --- a/internal/api/mocks.go +++ b/internal/api/mocks.go @@ -378,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") } @@ -537,7 +537,7 @@ func (q ReflectCustom) Query(request json.RawMessage) ([]byte, error) { case query.Capitalized != nil: resp.Msg = strings.ToUpper(query.Capitalized.Text) default: - return nil, errors.New("Unsupported query") + return nil, errors.New("unsupported query") } return json.Marshal(resp) } diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index dcda5718d..ab86403b5 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -749,10 +749,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/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/json_size.go b/types/json_size.go index cfd9775eb..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 diff --git a/types/queries.go b/types/queries.go index 09c0e17ae..37cde83f1 100644 --- a/types/queries.go +++ b/types/queries.go @@ -20,7 +20,7 @@ type queryResultImpl struct { 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)) From 703cc45ab9432a8a72c4b5238e0c942dfb2d00f0 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 25 Apr 2025 09:57:00 +0700 Subject: [PATCH 19/26] disable gocritic temporarily --- .golangci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 4ff99be7d..6f68e8413 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,7 +19,6 @@ linters: - unused # Detect unused constants, variables, functions and types # Additional recommended linters - - gocritic # A more opinionated linter - gosec # Security checker - misspell # Find commonly misspelled words - bodyclose # Check HTTP response bodies are closed @@ -31,6 +30,8 @@ linters: # enable lager because changes are huge # - revive + # - gocritic # A more opinionated linter + settings: gocritic: @@ -38,6 +39,7 @@ linters: disabled-checks: - dupSubExpr - dupImport + - paramTypeCombine From 2e9424b9c89c9c7ea098a862de65a7e5a10a30dd Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 25 Apr 2025 10:05:35 +0700 Subject: [PATCH 20/26] update linter for v2 --- .golangci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 6f68e8413..0c1120cfd 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,7 +56,7 @@ linters: - examples$ issues: - exclude-use-default: false + exclude-use-default-linters: false max-issues-per-linter: 0 max-same-issues: 0 From 71448a86246eff56bb6a4cf6a415847f2d45738e Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 25 Apr 2025 10:07:24 +0700 Subject: [PATCH 21/26] really get it working for v2 --- .golangci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 0c1120cfd..10609a2b4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,7 +56,6 @@ linters: - examples$ issues: - exclude-use-default-linters: false max-issues-per-linter: 0 max-same-issues: 0 From 6838413011e2bb8f2fa825dfaf598340caf3df84 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 27 Apr 2025 14:53:01 +0700 Subject: [PATCH 22/26] Update internal/api/lib_test.go Co-authored-by: Christoph Otter --- internal/api/lib_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index d619d3775..cf6a7e689 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -821,7 +821,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, uint64(maxGas), totalCost) + require.Equal(t, maxGas, totalCost) } func BenchmarkContractCall(b *testing.B) { From a574461b62958c8ac6cbb2eaeecdd685e96e9b1f Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 27 Apr 2025 19:00:19 +0700 Subject: [PATCH 23/26] bring back useful comments --- internal/api/callbacks.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/api/callbacks.go b/internal/api/callbacks.go index 0bb1497d0..392d72109 100644 --- a/internal/api/callbacks.go +++ b/internal/api/callbacks.go @@ -272,6 +272,11 @@ func cScan(ptr *C.db_t, gasMeter *C.gas_meter_t, usedGas *cu64, start C.U8SliceV //export cNext func cNext(ref C.IteratorReference, gasMeter *C.gas_meter_t, usedGas *cu64, key *C.UnmanagedVector, val *C.UnmanagedVector, errOut *C.UnmanagedVector) (ret C.GoError) { + // typical usage of iterator + // for ; itr.Valid(); itr.Next() { + // 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 From c0d7ea2e5d639f7aadb6cd55b880d209362a150e Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Wed, 30 Apr 2025 03:31:34 +0700 Subject: [PATCH 24/26] use C.ErrnoVALUE_Success --- internal/api/lib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/lib.go b/internal/api/lib.go index 7c5afdecd..42eeda915 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -381,7 +381,7 @@ func MigrateWithInfo( errmsg := uninitializedUnmanagedVector() res, err := C.migrate_with_info(cache.ptr, cs, e, m, i, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) - if err != nil && err.(syscall.Errno) != 0 { + if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } From ff1c56d73a053b3c648741dad5156739ce6cc5b2 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Wed, 30 Apr 2025 04:08:04 +0700 Subject: [PATCH 25/26] I cannot argue about this :) --- internal/api/lib_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index c81d47f8b..73cd765aa 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -29,13 +29,7 @@ const ( var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3"} func TestInitAndReleaseCache(t *testing.T) { - 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) - } - }() + tmpdir := t.TempDir() config := types.VMConfig{ Cache: types.CacheOptions{ From c9bc165eab297125779762aa29757d5bc6076017 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Wed, 30 Apr 2025 14:03:14 +0700 Subject: [PATCH 26/26] eliminate double declaration of CapitalizedResponse --- internal/api/lib_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index 73cd765aa..17c322f80 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -1369,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)