(Specifications) A bug in specifications with no direct impact on client implementations
Description
Brief/Intro
The response of null in the rpc package of Ethereum client Erigon ( https://github.com/erigontech/erigon ) could not be presented correctly due to the unmarshalling of the null in the CallContext.
In the rpc package, the CallContext is used to perform a JSON-RPC call and unmarshal the returned result. However, the unmarshalling for the result of null is not correctly performed:
// CallContext performs a JSON-RPC call with the given arguments. If the context is
// canceled before the call has successfully returned, CallContext returns immediately.
//
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr {
return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result)
}
msg, err := c.newMessage(method, args...)
if err != nil {
return err
}
op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
if c.isHTTP {
err = c.sendHTTP(ctx, op, msg)
} else {
err = c.send(ctx, op, msg)
}
if err != nil {
return err
}
// dispatch has accepted the request and will close the channel when it quits.
switch resp, err := op.wait(ctx, c); {
case err != nil:
return err
case resp.Error != nil:
return resp.Error
case len(resp.Result) == 0:
return ErrNoResult
default:
return json.Unmarshal(resp.Result, &result)
}
}
As mentioned in the geth(go-ethereum) PR: https://github.com/ethereum/go-ethereum/pull/26701
The function already checks that the result is either nil or a pointer type, so the extra reference operator is unnecessary. This actually causes a bug where nulls are not unmarshalled correctly into json.RawMessage.
It is worth noted a similar issue has been fixed in geth (go-ethereum) by fixing the result unmarshalling dependent of null:
The output shows the Expected non-nil result error message when the result is nil.
=== RUN TestNullResponse
[WARN] [12-02|12:17:37.643] Cannot register RPC callback [invalidRets1] - error must the last return value
[WARN] [12-02|12:17:37.643] Cannot register RPC callback [invalidRets2] - error must the last return value
[WARN] [12-02|12:17:37.643] Cannot register RPC callback [invalidRets3] - maximum 2 return values are allowed, got 3
client_test.go:140: Expected non-nil result
--- FAIL: TestNullResponse (0.00s)
FAIL