Skip to content

feat(http): support numeric field aliases in json parsing - #9

Open
waynercheung wants to merge 1 commit into
release_v4.8.3from
feature/support_numeric_field_alias
Open

waynercheung wants to merge 1 commit into
release_v4.8.3from
feature/support_numeric_field_alias

Conversation

@waynercheung

Copy link
Copy Markdown
Owner

What does this PR do?

JsonFormat.mergeField resolves a single-digit JSON key such as "2" to the field with that number when the name lookup fails, but it then marked that field as unknown. For a message-typed field the flag made handleObject read the value as a hex-encoded protobuf message instead of parsing a JSON object, so the two spellings of one field accepted different syntaxes:

input before after
{"owner":{"keys":[{}]}} accepted unchanged
{"2":{"keys":[{}]}} rejected, Expected string. accepted, identical to the field name
{"2":"3a00"} parsed as protobuf wire data rejected, Expected "{".
{"2":["3a00"]} / {"owner":{"7":"1001"}} parsed as wire data rejected
{"1":"<address>"}, {"2":null}, {"2":[]} — unchanged

This PR drops the flag and the hex branch, together with the parameters, import and comment that become dead, so a numeric alias goes through the same object parser as the field name at any nesting level and for singular, repeated and map fields alike.

Scope is unchanged: only fields 1 to 9 can be addressed by number (DIGITS matches a single character), and a key that resolves to no field is still skipped. Scalar and bytes aliases, unknown keys, null and empty arrays keep their current behaviour. This applies to protobuf fields only; the wallet endpoints still read raw_data, contract, parameter, value and type as literal JSON keys.

The commit also reorders a few statements in RateLimiterServlet.service: the request url is computed before the rate limiter checks, and the GET setup is paired with the cleanup that follows it by wrapping both in the outer try, leaving the request handling in an inner try with the catch clauses it already had.

Why are these changes required?

One field had two different input syntaxes depending on how it was spelled, and the hex form is an artifact of the protobuf-java-format code this class was forked from in 2018: the node never emits it (unknown fields are not printed), no in-repo caller produces it, and com.google.protobuf.util.JsonFormat has no equivalent. Making both spellings parse the same way removes the divergence and keeps numeric aliases usable in the form clients would expect.

BREAKING CHANGE

A message-typed field addressed by its field number now accepts JSON objects instead of hex-encoded protobuf strings:

  • direct JSON-to-protobuf merges reject the old string form with a parse error;
  • during per-contract parsing, Util.packTransaction omits contracts that fail to parse, the remaining contracts continue through normal endpoint validation, and if none remain the endpoint's existing empty-contract handling applies;
  • parse errors during the final transaction merge instead make Util.packTransaction return null, invoking the caller's existing error handling;
  • JSON-RPC ABI parsing rejects this form with invalid abi.

Clients using numeric aliases should send the same JSON objects they would send for field names. During a rolling upgrade, use field names.

This PR has been tested by:

  • Unit Tests

JsonFormatNumericAliasTest (new, 23 cases) covers alias-equals-name for singular, repeated, nested, map and map-entry fields, visible=true address and name-string bytes, enum by field and value in both spellings, null / [] / {}, rejection of the string form in every position, the single-digit range boundary, mixed name-and-alias documents, and partial state after a failed element. UtilTest adds 4 cases for Util.packTransaction (alias equivalence, dropped contract, only-the-failing-contract dropped, envelope-level failure returning null), JsonrpcServiceTest adds 2 for the JSON-RPC ABI path, and RateLimiterServletTest adds 2.

Targeted run on JDK 17: JsonFormatNumericAliasTest 23, JsonFormatTest 27, JsonFormatEscapeTest 33, JsonFormatInt64AsStringTest 13, UtilTest 10, RateLimiterServletTest 14, RateLimiterServletInt64Test 6, JsonrpcServiceTest 30 — 156 tests, 0 failures. checkstyleMain and checkstyleTest clean.

Follow up

None.

Extra details

Behaviour verified against the base build for every case above: 16 of the 23 new parser tests fail without this change, and the 7 that pass are the invariant guards for the behaviours listed as unchanged.

JsonFormat.mergeField resolves a single-digit key such as "2" to the
field with that number, but then marked that field as unknown. For a
message-typed field the flag made handleObject read the value as a
hex-encoded protobuf message instead of parsing a JSON object, so the
two spellings of one field accepted different syntaxes.

Drop the flag and the hex branch so a single-digit alias is parsed by
JsonFormat exactly like the field name, at any nesting level and for
singular, repeated and map fields alike. The alias range is unchanged:
only fields 1 to 9 can be addressed by number, and a key that resolves
to no field is still skipped. Scalar and bytes aliases, unknown keys,
null and empty arrays keep their current behaviour. This applies to
protobuf fields only; the wallet endpoints still read raw_data,
contract, parameter, value and type as literal JSON keys.

Also compute the request url before the rate limiter checks in
RateLimiterServlet, and pair the GET setup with the cleanup that
follows it by wrapping both in the outer try, leaving the request
handling in an inner try with the catch clauses it already had.

BREAKING CHANGE: a message-typed field addressed by its field number
now accepts JSON objects instead of hex-encoded protobuf strings.
Direct JSON-to-protobuf merges reject the old string form with a parse
error. During per-contract parsing, Util.packTransaction omits
contracts that fail to parse; remaining contracts continue through
normal endpoint validation, and if none remain the endpoint's existing
empty-contract handling applies. Parse errors during the final
transaction merge instead make Util.packTransaction return null,
invoking the caller's existing error handling. JSON-RPC ABI parsing
rejects this form with "invalid abi". Clients using numeric aliases
should send the same JSON objects they would send for field names.
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 this pull request may close these issues.

2 participants