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

rpc: implement JSON marshaling of BlockNumber #23324

Merged
merged 1 commit into from
Aug 25, 2021

Conversation

zenovich
Copy link
Contributor

@zenovich zenovich commented Aug 3, 2021

Currently rpc.BlockNumber is marshalled to JSON as a numeric value {"blockNumber":123} which is wrong because BlockNumber.UnmarshalJSON() wants it either to be hex-encoded or to have a special value ("earliest"/"latest"/"pending"). As a result, the chain rpc.BlockNumberOrHashWithNumber(123) -> json.Marshal() -> json.Unmarshal() fails with an error "cannot unmarshal object into Go value of type string".

The PR fixes the issue by adding BlockNumber.MarshalText() method which outputs either a hex-encoded number or a special string depending on the given block number.

Copy link
Contributor

@holiman holiman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I agree with the argument made in this PR, not sure if there are any consequences that we need to consider...?

Comment on lines +137 to +155
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number))
marshalled, err := json.Marshal(bnh)
if err != nil {
t.Fatal("cannot marshal:", err)
}
var unmarshalled BlockNumberOrHash
err = json.Unmarshal(marshalled, &unmarshalled)
if err != nil {
t.Fatal("cannot unmarshal:", err)
}
if !reflect.DeepEqual(bnh, unmarshalled) {
t.Fatalf("wrong result: expected %v, got %v", bnh, unmarshalled)
}
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might skip a layer of test nesting:

Suggested change
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number))
marshalled, err := json.Marshal(bnh)
if err != nil {
t.Fatal("cannot marshal:", err)
}
var unmarshalled BlockNumberOrHash
err = json.Unmarshal(marshalled, &unmarshalled)
if err != nil {
t.Fatal("cannot unmarshal:", err)
}
if !reflect.DeepEqual(bnh, unmarshalled) {
t.Fatalf("wrong result: expected %v, got %v", bnh, unmarshalled)
}
})
}
}
for _, test := range tests {
bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number))
marshalled, err := json.Marshal(bnh)
if err != nil {
t.Fatal("cannot marshal:", err)
}
var unmarshalled BlockNumberOrHash
if err = json.Unmarshal(marshalled, &unmarshalled); err != nil {
t.Fatal("cannot unmarshal:", err)
}
if !reflect.DeepEqual(bnh, unmarshalled) {
t.Fatalf("wrong result: expected %v, got %v", bnh, unmarshalled)
}
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test.name would become unused

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two pros of test nesting here:

  1. failure related to one dataset doesn't suppress checking of other datasets,
  2. in case of failure, the report clearly shows for which of data sets the code doesn't work correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--- FAIL: TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal (0.00s)
    --- FAIL: TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal/pending (0.00s)
        types_test.go:148: cannot unmarshal: json: cannot unmarshal object into Go value of type string
    --- FAIL: TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal/latest (0.00s)
        types_test.go:148: cannot unmarshal: json: cannot unmarshal object into Go value of type string

@zenovich zenovich requested a review from holiman August 3, 2021 15:33
@fjl fjl changed the title Marshal rpc.BlockNumber to JSON correctly rpc: implement JSON marshaling of BlockNumber Aug 9, 2021
@fjl fjl self-assigned this Aug 18, 2021
@fjl fjl added this to the 1.10.9 milestone Aug 25, 2021
@fjl fjl merged commit 7c4cad0 into ethereum:master Aug 25, 2021
sidhujag pushed a commit to sidhujag/go-ethereum that referenced this pull request Aug 25, 2021
Currently rpc.BlockNumber is marshalled to JSON as a numeric value, which is
wrong because BlockNumber.UnmarshalJSON() wants it to either be hex-encoded
or string "earliest"/"latest"/"pending". As a result, the call chain

    rpc.BlockNumberOrHashWithNumber(123) -> json.Marshal() -> json.Unmarshal()

fails with error "cannot unmarshal object into Go value of type string".
yongjun925 pushed a commit to DODOEX/go-ethereum that referenced this pull request Dec 3, 2022
Currently rpc.BlockNumber is marshalled to JSON as a numeric value, which is
wrong because BlockNumber.UnmarshalJSON() wants it to either be hex-encoded
or string "earliest"/"latest"/"pending". As a result, the call chain

    rpc.BlockNumberOrHashWithNumber(123) -> json.Marshal() -> json.Unmarshal()

fails with error "cannot unmarshal object into Go value of type string".
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.

3 participants