neo-project/neo-vm

Using opcode `LDSFLD0` instead of `LDSFLD 00` leads to unexpected return type.

Closed this issue ยท 6 comments

The following (meaningless) contract produces the script below when compiled with neon (version neo3-preview3).

public class Contract : SmartContract
{
    static byte[] owner = "AJunErzotcQTNWP2qktA7LgkXZVdHea97H".ToScriptHash();

    public static bool main()
    {
        if(!Runtime.CheckWitness(owner)) {
            return false;
        }
        return true;
    }
}
LDSFLD 00
SYSCALL f827ec8c
JMPIF_L 07000000
PUSH0
RET
PUSH1
RET

INITSSLOT 01
PUSHDATA1 20 226730eaeec8e3315468f57153e3b08789cc45cc
STSFLD0
RET

And this is the same script as a hex string: 215f002141f827ec8c25070000001040114056010c14226730eaeec8e3315468f57153e3b08789cc45cc6040

Our neow3j compiler produces a same script with the only difference that the LDSFLD 00 instruction is replaced with LDSFLD0.
This is a valid instruction and as far as i know, does the same as LDSFLD 00.
But when the script using LDSFLD0 is executed - the only method on the contract is called - the returned value is null and the StackItemType is ANY.
Once we replace the instruction LDSFLD0 with LDSFLD 00, the returned value is as expected, i.e. 1 or 0 depending if the transaction signer is the contract owner.

Having LDSFLD0 in the above script instead of LDSFLD 00 should not make any difference.

Maybe this issue is the real cause of #368.

I would like to hear the opinion of @shargon @Tommo-L and others.

We will improve this compilation (neo-project/neo-devpack-dotnet#382), but the logic for LDSFLD 00 and LDSFLD0 it's the same

case OpCode.LDSFLD6:
{
ExecuteLoadFromSlot(CurrentContext.StaticFields, instruction.OpCode - OpCode.LDSFLD0);
break;
}
case OpCode.LDSFLD:
{
ExecuteLoadFromSlot(CurrentContext.StaticFields, instruction.TokenU8);

Maybe this issue is the real cause of #368.

I would like to hear the opinion of @shargon @Tommo-L and others.

It's strange for me, we'll test it.

I have tested it on neo3-preview3 testnet, both contracts return the same result:

neo> invoke 0xca42bd2e02e9b680b44cf7fd2badb02beaa14092 main []
Invoking script with: '10c00c046d61696e0c149240a1ea2bb0ad2bfdf74cb480b6e9022ebd42ca41627d5b52'
VM State: HALT
Gas Consumed: 0.0103953
Result Stack: [{"type":"Integer","value":"0"}]
Relay tx(no|yes): no
neo> invoke 0x1e394dd787a02b95482304c387a88b055c972b5d main []
Invoking script with: '10c00c046d61696e0c145d2b975c058ba887c3042348952ba087d74d391e41627d5b52'
VM State: HALT
Gas Consumed: 0.0103956
Result Stack: [{"type":"Integer","value":"0"}]
Relay tx(no|yes):   

You can getcontractstate and check my contracts.

0xca42bd2e02e9b680b44cf7fd2badb02beaa14092, the hex string is 5702005f0041f827ec8c10b370682606107122041171694056010c14f4e43bf177ad38113b29524cace20cbed17951b96040

image

0x1e394dd787a02b95482304c387a88b055c972b5d, the hex string is 570200582141f827ec8c10b370682606107122041171694056010c14f4e43bf177ad38113b29524cace20cbed17951b96040

image

The only difference is that 5f00 and 5821.

Hey @ProDog, @shargon, @Tommo-L, Thanks a lot for looking into it.
I was mistaken about this issue. Neo-vm is not at fault. The problem was that the contract manifests that i produced (from Java contracts) had wrong method offsets when using the ToScriptHash() ๐Ÿ˜‘ The offset would sometimes point outside of the script's bounds leading to weird behavior.
Thus, this issue can be closed.

But, one question remains. Shouldn't the neo-vm exit in state FAULT when the method offset points to an address outside of the script's bounds? Should I create an issue for this behavior?

Hey @ProDog, @shargon, @Tommo-L, Thanks a lot for looking into it.
I was mistaken about this issue. Neo-vm is not at fault. The problem was that the contract manifests that i produced (from Java contracts) had wrong method offsets when using the ToScriptHash() ๐Ÿ˜‘ The offset would sometimes point outside of the script's bounds leading to weird behavior.
Thus, this issue can be closed.

This will be solved with neo-project/neo-devpack-dotnet#382

But, one question remains. Shouldn't the neo-vm exit in state FAULT when the method offset points to an address outside of the script's bounds? Should I create an issue for this behavior?

You can open a issue, at now it's the same as RET