Skip to content

Expected JSON types are not enforced (or anything can be a string) #2349

@roman-khimov

Description

@roman-khimov

Describe the bug
JSON parser used by C# implementation is not enforcing expected value types which can lead to surprising effects.

To Reproduce
Test-invoke this script on preview5 node:
DPJ7Im5hbWUiOnsiYSI6ImIifSwiYWJpIjp7Im1ldGhvZHMiOlt7Im5hbWUiOiJfaW5pdGlhbGl6ZSIsIm9mZnNldCI6MCwicGFyYW1ldGVycyI6W10sInJldHVybnR5cGUiOiJWb2lkIiwic2FmZSI6ZmFsc2V9XSwiZXZlbnRzIjpbXX0sImdyb3VwcyI6W10sInBlcm1pc3Npb25zIjpbeyJjb250cmFjdCI6IioiLCJtZXRob2RzIjoiKiJ9XSwic3VwcG9ydGVkc3RhbmRhcmRzIjpbXSwidHJ1c3RzIjpbXSwiZXh0cmEiOm51bGx9Cg1AAU5FRjNuZW8tZ28tMC45My4wLXByZS0yMTMtZzFiY2EzNmE1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADyVgIMFFx6HTget4LRd8iMo1Q6nW5Dm9f42zBgEGFB6X04oCFhQFcCAnkmNAwsX2RlcGxveSBtZXRob2QgY2FsbGVkIGJlZm9yZSBjb250cmFjdCB1cGRhdGU0ZUVADC5fZGVwbG95IG1ldGhvZCBjYWxsZWQgYmVmb3JlIGNvbnRyYWN0IGNyZWF0aW9uNDFFQFcEAFkAICgGENsgQFhB+CfsjCEmGAwOVmVyaWZpZWQgT3duZXJBz+dHliERQFcDAVkAQCgGENsgQHhBz+dHliERQFcDAVkAQCgGENsgQAwFRXZlbnR4EcBQQZUBb2EhEUB24aSbEsAfDAZkZXBsb3kMFEMOn2+zE6jTordhO2eDCdHX1wGlQWJ9W1I=

It tries to deploy some NEF with this manifest:

{
   "name" : {
      "a" : "b"
   },
   "supportedstandards" : [],
   "abi" : {
      "methods" : [
         {
            "offset" : 0,
            "returntype" : "Void",
            "safe" : false,
            "name" : "_initialize",
            "parameters" : []
         }
      ],
      "events" : []
   },
   "trusts" : [],
   "permissions" : [
      {
         "contract" : "*",
         "methods" : "*"
      }
   ],
   "extra" : null,
   "groups" : []
}

where name field is an object instead of a string.

Expected behavior
neo-go rejects it with

  "state": "FAULT",
  "gasconsumed": "1001014570",
...
  "stack": [],
  "exception": "error encountered at instruction 1 (SYSCALL): invalid manifest: json: cannot unmarshal object into Go struct field Manifest.name of type string"

C# node accepts it:

  "state": "HALT",
  "gasconsumed": "1001014570",
...
  "stack": [
    {
      "type": "Array",
      "value": [
        {
          "type": "Integer",
          "value": "8"
        },
        {
          "type": "Integer",
          "value": "0"
        },
        {
          "type": "ByteString",
          "value": "0AfhcO5IlXn2V6TYAFA/4FBCdAA="
        },
        {
          "type": "ByteString",
          "value": "TkVGM25lby1nby0wLjkzLjAtcHJlLTIxMy1nMWJjYTM2YTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPJWAgwUXHodOB63gtF3yIyjVDqdbkOb1/jbMGAQYUHpfTigIWFAVwICeSY0DCxfZGVwbG95IG1ldGhvZCBjYWxsZWQgYmVmb3JlIGNvbnRyYWN0IHVwZGF0ZTRlRUAMLl9kZXBsb3kgbWV0aG9kIGNhbGxlZCBiZWZvcmUgY29udHJhY3QgY3JlYXRpb240MUVAVwQAWQAgKAYQ2yBAWEH4J+yMISYYDA5WZXJpZmllZCBPd25lckHP50eWIRFAVwMBWQBAKAYQ2yBAeEHP50eWIRFAVwMBWQBAKAYQ2yBADAVFdmVudHgRwFBBlQFvYSERQHbhpJs="
        },
        {
          "type": "Struct",
          "value": [
            {
              "type": "ByteString",
              "value": "eyJhIjoiYiJ9"
            },
            {
              "type": "Array",
              "value": []
            },
            {
              "type": "Array",
              "value": []
            },
            {
              "type": "Struct",
              "value": [
                {
                  "type": "Array",
                  "value": [
                    {
                      "type": "Struct",
                      "value": [
                        {
                          "type": "ByteString",
                          "value": "X2luaXRpYWxpemU="
                        },
                        {
                          "type": "Array",
                          "value": []
                        },
                        {
                          "type": "Integer",
                          "value": "255"
                        },
                        {
                          "type": "Integer",
                          "value": "0"
                        },
                        {
                          "type": "Boolean",
                          "value": false
                        }
                      ]
                    }
                  ]
                },
                {
                  "type": "Array",
                  "value": []
                }
              ]
            },
            {
              "type": "Array",
              "value": [
                {
                  "type": "Struct",
                  "value": [
                    {
                      "type": "Any"
                    },
                    {
                      "type": "Any"
                    }
                  ]
                }
              ]
            },
            {
              "type": "Array",
              "value": []
            },
            {
              "type": "ByteString",
              "value": "bnVsbA=="
            }
          ]
        }
      ]
    }
  ]

Notice that there is a eyJhIjoiYiJ9 string (which is {"a":"b"}) in the name field of the manifest stack item which is wrong, the original manifest didn't have this string in the name field, it had an object with one field. So this manifest should've been rejected, but it's not.

(Optional) Additional context
I see several potential fixes for this:

  • a different parsing strategy
  • leaving parsing as is but checking types after parsing (not using overloaded AsString() basically), though this has to be done everywhere the parser is used
  • adding additional schema checking before the parser

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions