neo-project / neo-devpack-dotnet

NEO Development Pack
MIT License
79 stars 102 forks source link

Mint multiple NEP11 tokens causes Peek out of bounds: 0/0 #1224

Open GreenCakes-png opened 1 week ago

GreenCakes-png commented 1 week ago

Copied this example "examples/Example.SmartContract.SampleRoyaltyNEP11Token"

I added a method to mint multiple tokens at once like this:

public static void MultiMint(UInt160 to) { Mint(to); Mint(to); Mint(to); Mint(to); }

However when I call this method, the NEO CLI throws "Peak out of bounds..." error

image

Could someone help identify the cause or suggest a solution? Is it not possible to mint multiple tokens at once?

Jim8y commented 1 week ago

I'll take a look

Hecate2 commented 1 week ago

Could you provide any contract source code, the compiler version, or the .nef, .manifest.json, .nefdbgnfo, dumpnef? Also you may try neo-fairy-test.

Jim8y commented 1 week ago

@shargon can you please check this logic here, the problem is caused by this check:

https://github.com/neo-project/neo-devpack-dotnet/blob/65654df63f60c436f1c8d292975a6bbe831b652d/src/Neo.Compiler.CSharp/MethodConvert/PropertyConvert.cs#L133

if you call a storebacked property twice, you will be able to trigger this problem, cause the first call will initialize the property and make it none null, then the second call to the property will jump all the way to the bottom of the get method.

Jim8y commented 1 week ago

simply do this will reproduce the issue.

        public static void MultiMint(UInt160 to)
        {
            TotalSupply++;
            TotalSupply++;
        }
shargon commented 1 week ago

@shargon can you please check this logic here, the problem is caused by this check:

https://github.com/neo-project/neo-devpack-dotnet/blob/65654df63f60c436f1c8d292975a6bbe831b652d/src/Neo.Compiler.CSharp/MethodConvert/PropertyConvert.cs#L133

if you call a storebacked property twice, you will be able to trigger this problem, cause the first call will initialize the property and make it none null, then the second call to the property will jump all the way to the bottom of the get method.

this is supposed to be the instance, and because is static, should be null :S

Hecate2 commented 1 week ago
CurrentScriptHash=0x01f31fb35734aa143f1274da79129acf97309561[SampleRoyaltyNEP11Token]
EntryScriptHash=0x1f4435477ad4edeb6affd95bff88714f3ec66809
   at Neo.VM.ExecutionEngine.ExecuteNext() in C:\Users\RhantolkYtriHistoria\NEO\neo-node-mainnet\src\Neo.VM\ExecutionEngine.cs:line 148
    InstructionPointer=63, OpCode , Script Length=63 0x1f4435477ad4edeb6affd95bff88714f3ec66809[]
File test-compiler.cs, line 43: to
    InstructionPointer=937, OpCode LDARG0, Script Length=1929 0x01f31fb35734aa143f1274da79129acf97309561[SampleRoyaltyNEP11Token]
File test-compiler.cs, line 122: }
    InstructionPointer=1030, OpCode RET, Script Length=1929 0x01f31fb35734aa143f1274da79129acf97309561[SampleRoyaltyNEP11Token]
File Nep11Token.cs, line 116: TotalSupply++
    InstructionPointer=908, OpCode DUP, Script Length=1929 0x01f31fb35734aa143f1274da79129acf97309561[SampleRoyaltyNEP11Token]
   at Neo.VM.ExecutionEngine.ExecuteNext() in C:\Users\RhantolkYtriHistoria\NEO\neo-node-mainnet\src\Neo.VM\ExecutionEngine.cs:line 148
Peek out of bounds: 0/0

SampleRoyaltyNEP11Token.nef.txt

Hecate2 commented 1 week ago

A bug in the compilation of Neo.SmartContract.Framework.TokenContract.TotalSupply.get

# Method Start Neo.SmartContract.Framework.TokenContract.TotalSupply.get
# Code TokenContract.cs line 28: "[Safe] get;"
0018 LDSFLD0
# Code TokenContract.cs line 28: "[Safe] get;"
0019 ISNULL
# Code TokenContract.cs line 28: "[Safe] get;"
0020 JMPIFNOT 17 # pos: 43 (offset: 23)
...
# Method End Neo.SmartContract.Framework.TokenContract.TotalSupply.get
# Code TokenContract.cs line 28: "[Safe] get;"
0043 RET

Value of static field 0 is not returned. Related to ConvertStorageBackedProperty in compiler. But the code says:

                // AddInstruction(OpCode.DUP);
                AddInstruction(OpCode.ISNULL);
                // Ensure that no object was sent
                Jump(OpCode.JMPIFNOT_L, endTarget);

I am wondering if it is correct to write DUP ISNULL JMPIFNOT DROP