Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storage fails on a specific contract on Testnet t5 #2890

Closed
cschuchardt88 opened this issue Aug 28, 2023 · 3 comments · Fixed by #2892
Closed

Storage fails on a specific contract on Testnet t5 #2890

cschuchardt88 opened this issue Aug 28, 2023 · 3 comments · Fixed by #2892

Comments

@cschuchardt88
Copy link
Member

cschuchardt88 commented Aug 28, 2023

Describe the bug
Contract 0x12fbec62fb765ce3f55845106ccf3717716723ad on testnet t5 at block 2588120 is causing the problem. The contract permission is empty (has a bad stackitem in it)?

Abi shows no contract permissions * but neo seems to think it has two.
image

   at Neo.SmartContract.Manifest.ContractPermissionDescriptor..ctor(ReadOnlySpan`1 span)
   at Neo.SmartContract.Manifest.ContractManifest.<>c.<Neo.SmartContract.IInteroperable.FromStackItem>b__29_3(StackItem p)
   at System.Linq.Enumerable.SelectEnumerableIterator`2.ToArray()
   at Neo.SmartContract.Manifest.ContractManifest.Neo.SmartContract.IInteroperable.FromStackItem(StackItem stackItem)
   at Neo.SmartContract.ContractState.Neo.SmartContract.IInteroperable.FromStackItem(StackItem stackItem)
   at Neo.SmartContract.StorageItem.GetInteroperable[T]()
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.ToArray()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.OrderedEnumerable`1.GetEnumerator(Int32 minIdx, Int32 maxIdx)+MoveNext()
   at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source)
   at Neo.Plugins.RpcServer.FindTokenContracts(JArray _params)
   at Neo.Plugins.RpcServer.ProcessRequestAsync(HttpContext context, JObject request)

Value does not fall within the expected range. (Parameter 'span')

To Reproduce
Steps to reproduce the behavior:
Any application using this ListContracts function will be dead in the water.

var tokenList = NativeContract.ContractManagement.ListContracts(_neosystem.StoreView);
var vaildContracts = tokenList
    .OrderBy(o => o.Manifest.Name)
    .Skip((skip - 1) * take)
    .Take(take);
vaildContracts.Any() // <-- crashes when reading data

Rcp Server

RCP Request

{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "getstorage",
    "params": ["0x12fbec62fb765ce3f55845106ccf3717716723ad"]
}

RCP Response

{
    "jsonrpc": "2.0",
    "id": 1,
    "error": {
        "code": -2147024809,
        "message": "Value does not fall within the expected range. (Parameter 'span')"
    }
}

Expected behavior
This was working before for the longest time, and now it doesn't. Something somewhere broken Testnet T5. A stackitem, i think with bad data.

Need a hot fix

@cschuchardt88 cschuchardt88 changed the title Something broken testnet t5 ListContracts Crashes on Testnet t5 Aug 28, 2023
@cschuchardt88 cschuchardt88 changed the title ListContracts Crashes on Testnet t5 Storage fails on a specific contract on Testnet t5 Aug 29, 2023
@roman-khimov
Copy link
Contributor

Somewhat related to #2829, but a different thing, because NeoGo answers correctly (at least from the first glance).

$ curl -sd '{ "jsonrpc": "2.0", "id": 5, "method": "getcontractstate", "params": ["0x12fbec62fb765ce3f55845106ccf3717716723ad"] }' http://seed4t5.neo.org:20332 | json_pp
{
   "id" : 5,
   "error" : {
      "code" : -2147024809,
      "message" : "Value does not fall within the expected range. (Parameter 'span')"
   },
   "jsonrpc" : "2.0"
}
$ curl -sd '{ "jsonrpc": "2.0", "id": 5, "method": "getcontractstate", "params": ["0x12fbec62fb765ce3f55845106ccf3717716723ad"] }' https://rpc.t5.n3.nspcc.ru:20331 | json_pp
{
   "id" : 5,
   "jsonrpc" : "2.0",
   "result" : {
      "id" : 1264,
      "hash" : "0x12fbec62fb765ce3f55845106ccf3717716723ad",
      "nef" : {
         "magic" : 860243278,
         "script" : "VwADDBRYhxcRfgqoEHKvq3HS3Yn+fEuS/nh5DAhjYWxsYmFjawt6FcNKURRQ0EpRE1DQSlESUNBKURFQ0EpREFDQUB9QDAdyZXF1ZXN0UEFifVtSRUBXAARBm/ZnzhB4U0HmPxiEQZv2Z84RelNB5j8YhEGb9mfOEntTQeY/GIRAQfa0a+IQUEGSXegxQEH2tGviEVBBkl3oMUrYJgZFECIE2yFAQfa0a+ISUEGSXegxQA==",
         "source" : "",
         "compiler" : "neow3j-3.20.2",
         "tokens" : [],
         "checksum" : 334253959
      },
      "manifest" : {
         "abi" : {
            "events" : [],
            "methods" : [
               {
                  "name" : "request",
                  "safe" : false,
                  "offset" : 0,
                  "returntype" : "Void",
                  "parameters" : [
                     {
                        "type" : "String",
                        "name" : "url"
                     },
                     {
                        "type" : "String",
                        "name" : "filter"
                     },
                     {
                        "type" : "Integer",
                        "name" : "gasForResponse"
                     }
                  ]
               },
               {
                  "parameters" : [
                     {
                        "name" : "url",
                        "type" : "String"
                     },
                     {
                        "type" : "Any",
                        "name" : "userData"
                     },
                     {
                        "name" : "responseCode",
                        "type" : "Integer"
                     },
                     {
                        "name" : "response",
                        "type" : "ByteArray"
                     }
                  ],
                  "safe" : false,
                  "name" : "callback",
                  "offset" : 86,
                  "returntype" : "Void"
               },
               {
                  "parameters" : [],
                  "name" : "getStoredUrl",
                  "safe" : false,
                  "returntype" : "String",
                  "offset" : 129
               },
               {
                  "returntype" : "Integer",
                  "offset" : 142,
                  "name" : "getStoredResponseCode",
                  "safe" : false,
                  "parameters" : []
               },
               {
                  "safe" : false,
                  "name" : "getStoredResponse",
                  "offset" : 165,
                  "returntype" : "ByteArray",
                  "parameters" : []
               }
            ]
         },
         "supportedstandards" : [],
         "groups" : [],
         "name" : "CallOracleContract-6",
         "trusts" : [
            "0xfe924b7cfe89ddd271abaf7210a80a7e11178758",
            "*"
         ],
         "features" : {},
         "extra" : {},
         "permissions" : [
            {
               "contract" : "0xfe924b7cfe89ddd271abaf7210a80a7e11178758",
               "methods" : "*"
            },
            {
               "contract" : "*",
               "methods" : "*"
            }
         ]
      },
      "updatecounter" : 0
   }
}

But we have a stateroot mismatch since 2588120 (nspcc-dev/neo-go#3105), what a coincidence...

@cschuchardt88
Copy link
Member Author

because NeoGo answers correctly (at least from the first glance).

I think i spotted it?

"permissions" : [
            {
               "contract" : "0xfe924b7cfe89ddd271abaf7210a80a7e11178758",
               "methods" : "*"
            },
            {
               "contract" : "*",
               "methods" : "*"
            }
         ]

Also I'm currently unable to work on some projects that use the ListContracts method like above.

@roman-khimov
Copy link
Contributor

The problem is

   "trusts" : [
      "0xfe924b7cfe89ddd271abaf7210a80a7e11178758",
      "*"
   ]

Notice that this manifest is decoded successfully and deployment succeeds. It's also correct wrt NEP-15 requirements. But it needs to be stored and to do that it's converted to stackitems and then serialized. Trusts get serialized like this:

Trusts = WildcardContainer<ContractPermissionDescriptor>.FromJson(json["trusts"], u => ContractPermissionDescriptor.FromJson((JString)u)),

which is
/// <summary>
/// Converts the permission descriptor to byte array.
/// </summary>
/// <returns>The converted byte array. Or <see langword="null"/> if it is a wildcard.</returns>
public byte[] ToArray()
{
return Hash?.ToArray() ?? Group?.EncodePoint(true);
}

for each element. So we get

40 02 28 14 58 87 17 11 7e 0a a8 10 72 af ab 71 d2 dd  89 fe 7c 4b 92 fe 28 00

chunk for trusts in C# (an array of two elements, one is a byte array of length 20 and the other is an array of bytes of length zero), while NeoGo produces a bit different one:

40 02 28 14 58 87 17 11 7e 0a a8 10 72 af ab 71 d2 dd  89 fe 7c 4b 92 fe 00

which is an array of two elements, one is a byte array of length 20 and the other is Null.

Then when C# node tries to decode the thing it freaks out, because zero-length byte array is neither a proper hash, nor a proper key (and we have a broken state stored, similar to #2829). But it's absolutely not a problem in NeoGo case since there we have a Null item that can theoretically (but not practically in C# implementation) be decoded as wildcard trust.

Notice that permissions are not a problem since they're serialized differently with proper Null handling:

Contract.IsWildcard ? StackItem.Null : Contract.IsHash ? Contract.Hash.ToArray() : Contract.Group.ToArray(),
Methods.IsWildcard ? StackItem.Null : new Array(referenceCounter, Methods.Select(p => (StackItem)p)),

C# node needs to be fixed to store and read wildcard trusts appropriately. @steven1227, @shargon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants