diff --git a/ibc_test.go b/ibc_test.go index 2da754d14..63863b51b 100644 --- a/ibc_test.go +++ b/ibc_test.go @@ -1,5 +1,3 @@ -//go:build cgo && !nolink_libwasmvm - package cosmwasm import ( @@ -7,11 +5,10 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/CosmWasm/wasmvm/v2/internal/api" "github.com/CosmWasm/wasmvm/v2/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const IBC_TEST_CONTRACT = "./testdata/ibc_reflect.wasm" @@ -76,6 +73,7 @@ type AcknowledgeDispatch struct { } func toBytes(t *testing.T, v interface{}) []byte { + t.Helper() bz, err := json.Marshal(v) require.NoError(t, err) return bz @@ -90,9 +88,25 @@ func TestIBCHandshake(t *testing.T) { const CHANNEL_ID = "channel-432" vm := withVM(t) + + // First store the reflect contract that will be used by ibc_reflect + reflectWasm, err := os.ReadFile("./testdata/reflect.wasm") + require.NoError(t, err) + reflectChecksum, codeID, err := vm.StoreCode(reflectWasm, TESTING_GAS_LIMIT) + require.NoError(t, err) + t.Logf("Stored reflect contract with checksum: %v and code ID: %d", reflectChecksum, codeID) + + // Verify we can access the reflect contract code + reflectCode, err := vm.GetCode(reflectChecksum) + require.NoError(t, err) + t.Logf("Successfully retrieved reflect contract code, size: %d bytes", len(reflectCode)) + + // Now store the IBC contract checksum := createTestContract(t, vm, IBC_TEST_CONTRACT) - gasMeter1 := api.NewMockGasMeter(TESTING_GAS_LIMIT) + t.Logf("Stored IBC contract with checksum: %v", checksum) + deserCost := types.UFraction{Numerator: 1, Denominator: 1} + gasMeter1 := api.NewMockGasMeter(TESTING_GAS_LIMIT) // instantiate it with this store store := api.NewLookup(gasMeter1) goapi := api.NewMockAPI() @@ -105,11 +119,17 @@ func TestIBCHandshake(t *testing.T) { init_msg := IBCInstantiateMsg{ ReflectCodeID: REFLECT_ID, } - i, _, err := vm.Instantiate(checksum, env, info, toBytes(t, init_msg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT, deserCost) + t.Logf("Attempting to instantiate IBC contract with reflect code ID: %d", REFLECT_ID) + t.Logf("Instantiation message: %s", string(toBytes(t, init_msg))) + i, gas_used, err := vm.Instantiate(checksum, env, info, toBytes(t, init_msg), store, *goapi, querier, gasMeter1, TESTING_GAS_LIMIT, deserCost) + if err != nil { + t.Logf("Instantiation failed with gas used: %d", gas_used) + } require.NoError(t, err) + t.Logf("Instantiation response: %+v", i) assert.NotNil(t, i.Ok) iResponse := i.Ok - require.Equal(t, 0, len(iResponse.Messages)) + require.Empty(t, iResponse.Messages) // channel open gasMeter2 := api.NewMockGasMeter(TESTING_GAS_LIMIT) @@ -132,7 +152,7 @@ func TestIBCHandshake(t *testing.T) { require.NoError(t, err) require.NotNil(t, conn.Ok) connResponse := conn.Ok - require.Equal(t, 1, len(connResponse.Messages)) + require.Len(t, connResponse.Messages, 1) // check for the expected custom event expected_events := []types.Event{{ @@ -200,7 +220,7 @@ func TestIBCPacketDispatch(t *testing.T) { require.NoError(t, err) require.NotNil(t, conn.Ok) connResponse := conn.Ok - require.Equal(t, 1, len(connResponse.Messages)) + require.Len(t, connResponse.Messages, 1) id := connResponse.Messages[0].ID // mock reflect init callback (to store address) @@ -237,7 +257,7 @@ func TestIBCPacketDispatch(t *testing.T) { var accounts ListAccountsResponse err = json.Unmarshal(qResponse, &accounts) require.NoError(t, err) - require.Equal(t, 1, len(accounts.Accounts)) + require.Len(t, accounts.Accounts, 1) require.Equal(t, CHANNEL_ID, accounts.Accounts[0].ChannelID) require.Equal(t, REFLECT_ADDR, accounts.Accounts[0].Account) @@ -332,7 +352,7 @@ func TestIBCMsgGetChannel(t *testing.T) { require.Equal(t, msg1.GetChannel(), msg4.GetChannel()) require.Equal(t, msg1.GetChannel(), msg5.GetChannel()) require.Equal(t, msg1.GetChannel(), msg6.GetChannel()) - require.Equal(t, msg1.GetChannel().Endpoint.ChannelID, CHANNEL_ID) + require.Equal(t, CHANNEL_ID, msg1.GetChannel().Endpoint.ChannelID) } func TestIBCMsgGetCounterVersion(t *testing.T) { diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index 90c55a8c2..ff48a4d0e 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -26,7 +26,7 @@ const ( TESTING_CACHE_SIZE = 100 // MiB ) -var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3"} +var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3", "cosmwasm_1_4", "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2"} func TestInitAndReleaseCache(t *testing.T) { tmpdir, err := os.MkdirTemp("", "wasmvm-testing") diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index 3f66b71ea..edce0652d 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -1,5 +1,3 @@ -//go:build cgo && !nolink_libwasmvm - // This file contains the part of the API that is exposed when libwasmvm // is available (i.e. cgo is enabled and nolink_libwasmvm is not set). @@ -93,7 +91,8 @@ func (vm *VM) SimulateStoreCode(code WasmCode, gasLimit uint64) (Checksum, uint6 // StoreCodeUnchecked is the same as StoreCode but skips static validation checks. // Use this for adding code that was checked before, particularly in the case of state sync. func (vm *VM) StoreCodeUnchecked(code WasmCode) (Checksum, error) { - return api.StoreCodeUnchecked(vm.cache, code) + checksum, err := api.StoreCodeUnchecked(vm.cache, code) + return checksum, err } func (vm *VM) RemoveCode(checksum Checksum) error { diff --git a/lib_libwasmvm_test.go b/lib_libwasmvm_test.go index 4c25f69a0..58ea9f591 100644 --- a/lib_libwasmvm_test.go +++ b/lib_libwasmvm_test.go @@ -1,5 +1,3 @@ -//go:build cgo && !nolink_libwasmvm - package cosmwasm import ( @@ -9,21 +7,20 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/CosmWasm/wasmvm/v2/internal/api" "github.com/CosmWasm/wasmvm/v2/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( TESTING_PRINT_DEBUG = false TESTING_GAS_LIMIT = uint64(500_000_000_000) // ~0.5ms - TESTING_MEMORY_LIMIT = 32 // MiB + TESTING_MEMORY_LIMIT = 64 // MiB TESTING_CACHE_SIZE = 100 // MiB ) -var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator"} +var TESTING_CAPABILITIES = []string{"staking", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3", "cosmwasm_1_4", "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2"} const ( CYBERPUNK_TEST_CONTRACT = "./testdata/cyberpunk.wasm" @@ -31,6 +28,7 @@ const ( ) func withVM(t *testing.T) *VM { + t.Helper() tmpdir, err := os.MkdirTemp("", "wasmvm-testing") require.NoError(t, err) vm, err := NewVM(tmpdir, TESTING_CAPABILITIES, TESTING_MEMORY_LIMIT, TESTING_PRINT_DEBUG, TESTING_CACHE_SIZE) @@ -44,6 +42,7 @@ func withVM(t *testing.T) *VM { } func createTestContract(t *testing.T, vm *VM, path string) Checksum { + t.Helper() wasm, err := os.ReadFile(path) require.NoError(t, err) checksum, _, err := vm.StoreCode(wasm, TESTING_GAS_LIMIT) @@ -54,51 +53,53 @@ func createTestContract(t *testing.T, vm *VM, path string) Checksum { func TestStoreCode(t *testing.T) { vm := withVM(t) - // Valid hackatom contract - { - wasm, err := os.ReadFile(HACKATOM_TEST_CONTRACT) - require.NoError(t, err) - _, _, err = vm.StoreCode(wasm, TESTING_GAS_LIMIT) - require.NoError(t, err) - } - - // Valid cyberpunk contract - { - wasm, err := os.ReadFile(CYBERPUNK_TEST_CONTRACT) - require.NoError(t, err) - _, _, err = vm.StoreCode(wasm, TESTING_GAS_LIMIT) - require.NoError(t, err) - } - - // Valid Wasm with no exports - { - // echo '(module)' | wat2wasm - -o empty.wasm - // hexdump -C < empty.wasm - - wasm := []byte{0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00} - _, _, err := vm.StoreCode(wasm, TESTING_GAS_LIMIT) - require.ErrorContains(t, err, "Error during static Wasm validation: Wasm contract must contain exactly one memory") - } - - // No Wasm - { - wasm := []byte("foobar") - _, _, err := vm.StoreCode(wasm, TESTING_GAS_LIMIT) - require.ErrorContains(t, err, "Wasm bytecode could not be deserialized") - } + hackatom, err := os.ReadFile(HACKATOM_TEST_CONTRACT) + require.NoError(t, err) - // Empty - { - wasm := []byte("") - _, _, err := vm.StoreCode(wasm, TESTING_GAS_LIMIT) - require.ErrorContains(t, err, "Wasm bytecode could not be deserialized") + specs := map[string]struct { + wasm []byte + expectedErr string + expectOk bool + }{ + "valid wasm contract": { + wasm: hackatom, + expectOk: true, + }, + "nil bytes": { + wasm: nil, + expectedErr: "Null/Nil argument: wasm", + expectOk: false, + }, + "empty bytes": { + wasm: []byte{}, + expectedErr: "Wasm bytecode could not be deserialized", + expectOk: false, + }, + "invalid wasm - random bytes": { + wasm: []byte("random invalid data"), + expectedErr: "Wasm bytecode could not be deserialized", + expectOk: false, + }, + "invalid wasm - corrupted header": { + // First 8 bytes of a valid wasm file, followed by random data + wasm: append([]byte{0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00}, []byte("corrupted content")...), + expectedErr: "Wasm bytecode could not be deserialized", + expectOk: false, + }, } - // Nil - { - var wasm []byte = nil - _, _, err := vm.StoreCode(wasm, TESTING_GAS_LIMIT) - require.ErrorContains(t, err, "Null/Nil argument: wasm") + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + checksum, _, err := vm.StoreCode(spec.wasm, TESTING_GAS_LIMIT) + if spec.expectOk { + require.NoError(t, err) + require.NotEmpty(t, checksum, "checksum should not be empty on success") + } else { + require.Error(t, err) + require.Contains(t, err.Error(), spec.expectedErr) + require.Empty(t, checksum, "checksum should be empty on error") + } + }) } } @@ -109,29 +110,58 @@ func TestSimulateStoreCode(t *testing.T) { require.NoError(t, err) specs := map[string]struct { - wasm []byte - err string + wasm []byte + expectedErr string + expectOk bool }{ - "valid hackatom contract": { - wasm: hackatom, + "valid wasm contract": { + wasm: hackatom, + expectOk: true, + }, + "nil bytes": { + wasm: nil, + expectedErr: "Null/Nil argument: wasm", + expectOk: false, + }, + "empty bytes": { + wasm: []byte{}, + expectedErr: "Wasm bytecode could not be deserialized", + expectOk: false, + }, + "invalid wasm - random bytes": { + wasm: []byte("random invalid data"), + expectedErr: "Wasm bytecode could not be deserialized", + expectOk: false, }, - "no wasm": { - wasm: []byte("foobar"), - err: "Wasm bytecode could not be deserialized", + "invalid wasm - corrupted header": { + // First 8 bytes of a valid wasm file, followed by random data + wasm: append([]byte{0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00}, []byte("corrupted content")...), + expectedErr: "Wasm bytecode could not be deserialized", + expectOk: false, + }, + "invalid wasm - no memory section": { + // Minimal valid wasm module without memory section + wasm: []byte{0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00}, + expectedErr: "Error during static Wasm validation: Wasm contract must contain exactly one memory", + expectOk: false, }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { checksum, _, err := vm.SimulateStoreCode(spec.wasm, TESTING_GAS_LIMIT) + if spec.expectOk { + require.NoError(t, err) + require.NotEmpty(t, checksum, "checksum should not be empty on success") - if spec.err != "" { - assert.ErrorContains(t, err, spec.err) - } else { - assert.NoError(t, err) - + // Verify the code was not actually stored _, err = vm.GetCode(checksum) - assert.ErrorContains(t, err, "Error opening Wasm file for reading") + require.Error(t, err) + require.Contains(t, err.Error(), "Error opening Wasm file for reading") + } else { + require.Error(t, err) + require.Contains(t, err.Error(), spec.expectedErr) + require.Empty(t, checksum, "checksum should be empty on error") } }) } @@ -187,7 +217,7 @@ func TestHappyPath(t *testing.T) { require.NoError(t, err) require.NotNil(t, i.Ok) ires := i.Ok - require.Equal(t, 0, len(ires.Messages)) + require.Empty(t, ires.Messages) // execute gasMeter2 := api.NewMockGasMeter(TESTING_GAS_LIMIT) @@ -198,7 +228,7 @@ func TestHappyPath(t *testing.T) { require.NoError(t, err) require.NotNil(t, h.Ok) hres := h.Ok - require.Equal(t, 1, len(hres.Messages)) + require.Len(t, hres.Messages, 1) // make sure it read the balance properly and we got 250 atoms dispatch := hres.Messages[0].Msg @@ -231,7 +261,7 @@ func TestEnv(t *testing.T) { require.NoError(t, err) require.NotNil(t, i.Ok) ires := i.Ok - require.Equal(t, 0, len(ires.Messages)) + require.Empty(t, ires.Messages) // Execute mirror env without Transaction env = types.Env{ @@ -311,11 +341,11 @@ func TestGetMetrics(t *testing.T) { require.NoError(t, err) require.NotNil(t, i.Ok) ires := i.Ok - require.Equal(t, 0, len(ires.Messages)) + require.Empty(t, ires.Messages) // GetMetrics 3 metrics, err = vm.GetMetrics() - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, uint32(0), metrics.HitsMemoryCache) require.Equal(t, uint32(1), metrics.HitsFsCache) require.Equal(t, uint64(1), metrics.ElementsMemoryCache) @@ -328,11 +358,11 @@ func TestGetMetrics(t *testing.T) { require.NoError(t, err) require.NotNil(t, i.Ok) ires = i.Ok - require.Equal(t, 0, len(ires.Messages)) + require.Empty(t, ires.Messages) // GetMetrics 4 metrics, err = vm.GetMetrics() - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, uint32(1), metrics.HitsMemoryCache) require.Equal(t, uint32(1), metrics.HitsFsCache) require.Equal(t, uint64(1), metrics.ElementsMemoryCache) @@ -344,7 +374,7 @@ func TestGetMetrics(t *testing.T) { // GetMetrics 5 metrics, err = vm.GetMetrics() - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, uint32(1), metrics.HitsMemoryCache) require.Equal(t, uint32(2), metrics.HitsFsCache) require.Equal(t, uint64(1), metrics.ElementsPinnedMemoryCache) @@ -358,11 +388,11 @@ func TestGetMetrics(t *testing.T) { require.NoError(t, err) require.NotNil(t, i.Ok) ires = i.Ok - require.Equal(t, 0, len(ires.Messages)) + require.Empty(t, ires.Messages) // GetMetrics 6 metrics, err = vm.GetMetrics() - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, uint32(1), metrics.HitsPinnedMemoryCache) require.Equal(t, uint32(1), metrics.HitsMemoryCache) require.Equal(t, uint32(2), metrics.HitsFsCache) @@ -377,7 +407,7 @@ func TestGetMetrics(t *testing.T) { // GetMetrics 7 metrics, err = vm.GetMetrics() - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, uint32(1), metrics.HitsPinnedMemoryCache) require.Equal(t, uint32(1), metrics.HitsMemoryCache) require.Equal(t, uint32(2), metrics.HitsFsCache) @@ -392,11 +422,11 @@ func TestGetMetrics(t *testing.T) { require.NoError(t, err) require.NotNil(t, i.Ok) ires = i.Ok - require.Equal(t, 0, len(ires.Messages)) + require.Empty(t, ires.Messages) // GetMetrics 8 metrics, err = vm.GetMetrics() - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, uint32(1), metrics.HitsPinnedMemoryCache) require.Equal(t, uint32(2), metrics.HitsMemoryCache) require.Equal(t, uint32(2), metrics.HitsFsCache) diff --git a/testdata/README.md b/testdata/README.md index f89f28f7e..76ca7aab3 100644 --- a/testdata/README.md +++ b/testdata/README.md @@ -1,8 +1,12 @@ +# Test Contracts + +## How to update + Update contracts via e.g. ```sh cd testdata -./download_releases.sh v0.14.0-beta2 +./download_releases.sh v2.2.0 ``` This will download the deployed builds [from GitHub releases](https://github.com/CosmWasm/cosmwasm/releases). diff --git a/testdata/cyberpunk.wasm b/testdata/cyberpunk.wasm index ea4d73e85..62f4d3d17 100644 Binary files a/testdata/cyberpunk.wasm and b/testdata/cyberpunk.wasm differ diff --git a/testdata/hackatom.wasm b/testdata/hackatom.wasm index 580f9cf13..7f0bc22f5 100644 Binary files a/testdata/hackatom.wasm and b/testdata/hackatom.wasm differ diff --git a/testdata/ibc_reflect.wasm b/testdata/ibc_reflect.wasm index a4ba226c6..1e8e7e318 100644 Binary files a/testdata/ibc_reflect.wasm and b/testdata/ibc_reflect.wasm differ diff --git a/testdata/queue.wasm b/testdata/queue.wasm index c3f22866d..bd725f7ca 100644 Binary files a/testdata/queue.wasm and b/testdata/queue.wasm differ diff --git a/testdata/reflect.wasm b/testdata/reflect.wasm index 6aeb62000..4c4af408a 100644 Binary files a/testdata/reflect.wasm and b/testdata/reflect.wasm differ