Files
chains/internal/codec/codec.go
zeekayandHanzo Dev 0ac784c33a codec, quantumvm: a lower-case name on the wire reaches an exported Go method
quantumvm registered node's JSON-RPC codec — the one that raises the initial
of the function named after the dot. That codec is now internal/codec, so the
spelling quantumvm serves, quantumvm.getBlock for Service.GetBlock, is decided
in this module.

The spelling is unchanged: an initial that is already upper-case is refused,
and an argument that will not read reports one message. identityvm, keyvm and
bridgevm register gorilla's plain codec and keep it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-30 14:54:14 -07:00

73 lines
2.2 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package codec speaks the JSON-RPC spelling this repo's VMs serve. A client
// names a method as class.function with a lower-case initial, and the codec
// hands the server the exported Go method that answers it, so
// quantumvm.getBlock reaches Service.GetBlock.
//
// A function whose initial is already upper-case is refused, so exactly one
// spelling reaches a service.
package codec
import (
"errors"
"fmt"
"net/http"
"strings"
"unicode"
"unicode/utf8"
"github.com/gorilla/rpc/v2"
"github.com/gorilla/rpc/v2/json2"
)
var (
errUppercaseMethod = errors.New("method must start with a non-uppercase letter")
errInvalidArg = errors.New("couldn't unmarshal an argument. Ensure arguments are valid and properly formatted. See documentation for example calls")
)
// New returns a JSON-RPC codec that raises the initial of the function named
// after the dot.
func New() rpc.Codec {
return codec{json2.NewCodec()}
}
type codec struct{ *json2.Codec }
func (c codec) NewRequest(r *http.Request) rpc.CodecRequest {
return &request{c.Codec.NewRequest(r).(*json2.CodecRequest)}
}
type request struct{ *json2.CodecRequest }
// Method names the Go method the request asks for: the class unchanged, and
// the function with its initial raised. A name carrying no dot, or one whose
// initial is not a rune, is passed through as it arrived.
func (r *request) Method() (string, error) {
method, err := r.CodecRequest.Method()
parts := strings.SplitN(method, ".", 2)
if len(parts) != 2 || err != nil {
return method, err
}
class, function := parts[0], parts[1]
initial, size := utf8.DecodeRuneInString(function)
if initial == utf8.RuneError {
return method, nil
}
if unicode.IsUpper(initial) {
return method, errUppercaseMethod
}
raised := string(unicode.ToUpper(initial))
return fmt.Sprintf("%s.%s%s", class, raised, function[size:]), nil
}
// ReadRequest reports one message for arguments it cannot read, so a caller
// learns its arguments are wrong and nothing further.
func (r *request) ReadRequest(args any) error {
if err := r.CodecRequest.ReadRequest(args); err != nil {
return errInvalidArg
}
return nil
}