diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index cec91d56c5..e6811c55f7 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -712,6 +712,32 @@ func TestInstantiateWithNonExistingCodeID(t *testing.T) { require.Nil(t, addr) } +func TestContractErrorRedacting(t *testing.T) { + ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) + + deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) + creator := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + keepers.Faucet.Fund(ctx, creator, deposit...) + example := StoreHackatomExampleContract(t, ctx, keepers) + + initMsg := HackatomExampleInitMsg{ + Verifier: []byte{1, 2, 3}, // invalid length + Beneficiary: RandomAccountAddress(t), + } + initMsgBz, err := json.Marshal(initMsg) + require.NoError(t, err) + + em := sdk.NewEventManager() + + _, _, err = keepers.ContractKeeper.Instantiate(ctx.WithEventManager(em), example.CodeID, creator, nil, initMsgBz, "demo contract 1", nil) + require.Error(t, err) + require.Contains(t, err.Error(), "addr_validate errored: invalid address") + + err = redactError(err) + // contract error should not be redacted + require.Contains(t, err.Error(), "addr_validate errored: invalid address") +} + func TestInstantiateWithContractDataResponse(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) diff --git a/x/wasm/keeper/msg_dispatcher.go b/x/wasm/keeper/msg_dispatcher.go index 814c6110b3..c670a23225 100644 --- a/x/wasm/keeper/msg_dispatcher.go +++ b/x/wasm/keeper/msg_dispatcher.go @@ -215,6 +215,12 @@ func redactError(err error) error { // sdk/5 is insufficient funds (on bank send) // (we can theoretically redact less in the future, but this is a first step to safety) codespace, code, _ := errorsmod.ABCIInfo(err, false) + + // Also do not redact any errors that are coming from the contract, + // as they are always deterministic + if codespace == types.DefaultCodespace && contains(types.ContractErrorCodes, code) { + return err + } return fmt.Errorf("codespace: %s, code: %d", codespace, code) } diff --git a/x/wasm/types/errors.go b/x/wasm/types/errors.go index 035d50d389..724c0c38fa 100644 --- a/x/wasm/types/errors.go +++ b/x/wasm/types/errors.go @@ -10,6 +10,10 @@ import ( var ( DefaultCodespace = ModuleName + // ContractErrorCodes are the error codes for errors returned by the contract + // Since contract execution is deterministic, the errors are also deterministic + ContractErrorCodes = []uint32{InstantiateErrorCode, ExecuteErrorCode, QueryErrorCode, MigrateErrorCode} + // Note: never use code 1 for any errors - that is reserved for ErrInternal in the core cosmos sdk // ErrCreateFailed error for wasm code that has already been uploaded or failed @@ -19,10 +23,10 @@ var ( ErrAccountExists = errorsmod.Register(DefaultCodespace, 3, "contract account already exists") // ErrInstantiateFailed error for rust instantiate contract failure - ErrInstantiateFailed = errorsmod.Register(DefaultCodespace, 4, "instantiate wasm contract failed") + ErrInstantiateFailed = errorsmod.Register(DefaultCodespace, InstantiateErrorCode, "instantiate wasm contract failed") // ErrExecuteFailed error for rust execution contract failure - ErrExecuteFailed = errorsmod.Register(DefaultCodespace, 5, "execute wasm contract failed") + ErrExecuteFailed = errorsmod.Register(DefaultCodespace, ExecuteErrorCode, "execute wasm contract failed") // ErrGasLimit error for out of gas ErrGasLimit = errorsmod.Register(DefaultCodespace, 6, "insufficient gas") @@ -34,13 +38,13 @@ var ( ErrNotFound = errorsmod.Register(DefaultCodespace, 8, "not found") // ErrQueryFailed error for rust smart query contract failure - ErrQueryFailed = errorsmod.Register(DefaultCodespace, 9, "query wasm contract failed") + ErrQueryFailed = errorsmod.Register(DefaultCodespace, QueryErrorCode, "query wasm contract failed") // ErrInvalidMsg error when we cannot process the error returned from the contract ErrInvalidMsg = errorsmod.Register(DefaultCodespace, 10, "invalid CosmosMsg from the contract") // ErrMigrationFailed error for rust execution contract failure - ErrMigrationFailed = errorsmod.Register(DefaultCodespace, 11, "migrate wasm contract failed") + ErrMigrationFailed = errorsmod.Register(DefaultCodespace, MigrateErrorCode, "migrate wasm contract failed") // ErrEmpty error for empty content ErrEmpty = errorsmod.Register(DefaultCodespace, 12, "empty") @@ -91,6 +95,14 @@ var ( ErrVMError = errorsmod.Register(DefaultCodespace, 29, "wasmvm error") ) +// Error codes for wasm contract errors +const ( + InstantiateErrorCode = 4 + ExecuteErrorCode = 5 + QueryErrorCode = 9 + MigrateErrorCode = 11 +) + // WasmVMErrorable mapped error type in wasmvm and are not redacted type WasmVMErrorable interface { // ToWasmVMError convert instance to wasmvm friendly error if possible otherwise root cause. never nil