neo-devpack-dotnet icon indicating copy to clipboard operation
neo-devpack-dotnet copied to clipboard

Cast to boolean is not working

Open RawichK opened this issue 3 years ago • 4 comments

bool val = (bool)myProperties["isExist"];
bool test1 = val == true ? true : false;
bool test2 = val == false ? true : false;

Both test1 and test2 have false value.

When switching to the disassembly view, the value in LOC1 is the value 1. So, the val == true is boiling down to 1 == true which fails.

RawichK avatar Aug 18 '22 02:08 RawichK

Can you provide the definition of myProperties?

erikzhang avatar Aug 25 '22 04:08 erikzhang

it is a Map<string, object> type.

Map<string, object> myProperties = new();
myProperties["isExist"] = true;

RawichK avatar Aug 25 '22 04:08 RawichK

public static bool testCast()
{
    Map<string, object> myProperties = new();
    myProperties["isExist"] = true;
    bool val = (bool)myProperties["isExist"];
    bool test1 = val == true ? true : false;
    return test1;
}

It returns true.

erikzhang avatar Aug 25 '22 23:08 erikzhang

I tried your snippet and it indeed returns true, my sample code cannot reflect the real case. Because the actual data is loaded from storage and somehow it has been stored as 1 even the field is type bool from the beginning. I have a workaround to use only bool test1 = val ? true : false; If you would like to investigate further, I can share my private repo with you. Otherwise, I think we can close this issue. Because I cannot reproduce the storage written issue that writes true as 1 in toolkit version (3.3.0)

RawichK avatar Aug 26 '22 03:08 RawichK

@RawichK Beware that casting can lead to erratic behavior, because C# Neo3 SDK may sometimes be invoked as an explicit operator, which is correct, or may be masked by some implicit C# conversion, which is incorrect, and seems to be the case for object directly to bool. On general, avoid casting and explicitly declare the intended behavior. Below, I provide an IntegerToBoolean function, that works correctly, and can be optimized to bytecode if needed (to save some gas). Note that testCase1 works correctly, but testCase2 is wrong... the reason is that (bool) casting does not actually convert the internal ByteString stack item into a Boolean stack item, leading to wrong interpretation in the smart contract. In this example, I cannot remove the (BigInteger) casting, but it is possible to improve the SDK to prevent such casting as well.

     public static event Action<string, bool> my_event_bool;
     
     public static bool IntegerToBool(BigInteger big) {
          return !(big == 0); 
      }

      public static bool testCast1() {
          Storage.Put(Storage.CurrentContext, "boolStored", 1);
          Map<string, object> myProperties = new();
          myProperties["isExist"] =  IntegerToBool((BigInteger)Storage.Get(Storage.CurrentContext, "boolStored"));
          bool val = (bool)myProperties["isExist"];
          // val is Boolean (true)
          my_event_bool("testCast1 val:", val);
          bool test1 = val == true ? true : false;
          // test1 is Boolean (true)
          my_event_bool("testCast1 final:", test1);
          return test1; // true (CORRECT)
      }

      public static bool testCast2()
      {
          Storage.Put(Storage.CurrentContext, "boolStored", 1);
          Map<string, object> myProperties = new();
          myProperties["isExist"] =  Storage.Get(Storage.CurrentContext, "boolStored");
          bool val = (bool)myProperties["isExist"];
          // val is ByteString (01)  
          my_event_bool("testCast2 val:", val);
          bool test1 = val == true ? true : false;
          // test1 is Boolean (false)
          my_event_bool("testCast2 final:", test1);
          return test1; // false (INCORRECT)
      }

igormcoelho avatar Aug 16 '23 19:08 igormcoelho

Thank you @igormcoelho for the detail explanations.

RawichK avatar Aug 19 '23 03:08 RawichK