quorum

package
v0.0.0-...-9e86e28 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 15, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package quorum verifies signatures returned by upstream providers when a client requests quorum reads (via quorum=N&quorum_required=n query params).

Each participating upstream returns a header in the strict form:

QR<N>-id-<request-id>: <provider-id>(<upstream-id>)_nonce_<nonce>_sig_<hex-signature>

All four components are required. The <provider-id> (before the parentheses) is used to look up the public key in provider_keys.yaml. The <upstream-id> (inside the parentheses) is the dshackle `source` and is what the signature is actually computed over. Any header whose value does not match this exact shape is rejected as malformed.

The signed message is produced with one of:

  • SHA256withECDSA over NIST P-256 (signature is DER-encoded ASN.1), or
  • SHA256withRSA / PKCS#1 v1.5 (signature is a fixed-size integer block)

over the string "DSHACKLESIG/<nonce>/<upstream-id>/<hex(sha256(result))>". The concrete algorithm is inferred from the type of the public key loaded from provider_keys.yaml.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrMissingSignatures      = errors.New("quorum: no QR signature headers present")
	ErrInsufficientSignatures = errors.New("quorum: not enough signatures returned")
	ErrUnknownProvider        = errors.New("quorum: no public key for provider")
	ErrInvalidSignature       = errors.New("quorum: signature verification failed")
	ErrMalformedHeader        = errors.New("quorum: malformed QR header")
	ErrUnexpectedRequestID    = errors.New("quorum: QR header request id does not match request")
	ErrNotSupported           = errors.New("quorum: quorum is not supported for this request")
)

Functions

func ToResponseError

func ToResponseError(err error) *protocol.ResponseError

func WithParams

func WithParams(ctx context.Context, p Params) context.Context

func WrapMessage

func WrapMessage(nonce uint64, source string, message []byte) []byte

WrapMessage reproduces dshackle's ResponseSigner.wrapMessage: "DSHACKLESIG/<nonce>/<source>/<hex(sha256(message))>". `source` here is what dshackle passed as `source` — the upstream id (what's in parens in the QR header prefix).

Types

type InsufficientSignaturesError

type InsufficientSignaturesError struct {
	Got      int
	Required int
}

InsufficientSignaturesError is returned when the upstream returned fewer valid signatures than the client required via `quorum_required`.

func (*InsufficientSignaturesError) Error

func (*InsufficientSignaturesError) Is

func (e *InsufficientSignaturesError) Is(target error) bool

type InvalidSignatureError

type InvalidSignatureError struct {
	ProviderID string
	Index      int
	Cause      error
}

InvalidSignatureError is returned when a QR signature does not verify against the provider's public key and the response body.

func (*InvalidSignatureError) Error

func (e *InvalidSignatureError) Error() string

func (*InvalidSignatureError) Is

func (e *InvalidSignatureError) Is(target error) bool

func (*InvalidSignatureError) Unwrap

func (e *InvalidSignatureError) Unwrap() error

type MalformedHeaderError

type MalformedHeaderError struct {
	HeaderName string
	Cause      error
}

MalformedHeaderError is returned when a QR* header does not match the strict `<providerID>(<upstreamID>)_nonce_<n>_sig_<hex>` shape.

func (*MalformedHeaderError) Error

func (e *MalformedHeaderError) Error() string

func (*MalformedHeaderError) Is

func (e *MalformedHeaderError) Is(target error) bool

func (*MalformedHeaderError) Unwrap

func (e *MalformedHeaderError) Unwrap() error

type NotSupportedError

type NotSupportedError struct {
	Reason string
}

NotSupportedError is returned when the current request shape cannot be served with quorum reads (no DRPC upstreams, sticky-send method, response is a stream, etc.). Propagated as-is to the client.

func (*NotSupportedError) Error

func (e *NotSupportedError) Error() string

func (*NotSupportedError) Is

func (e *NotSupportedError) Is(target error) bool

type Params

type Params struct {
	Quorum   int
	QuorumOf int
}

func FromContext

func FromContext(ctx context.Context) (Params, bool)

func ParamsFromQuery

func ParamsFromQuery(q url.Values) Params

func (Params) EncodeQuery

func (p Params) EncodeQuery() string

func (Params) Requested

func (p Params) Requested() bool

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

func DefaultRegistry

func DefaultRegistry() (*Registry, error)

func LoadRegistry

func LoadRegistry(data []byte) (*Registry, error)

func NewRegistry

func NewRegistry(keys map[string]crypto.PublicKey) *Registry

func (*Registry) Len

func (r *Registry) Len() int

func (*Registry) Lookup

func (r *Registry) Lookup(providerID string) (crypto.PublicKey, bool)

Lookup resolves a public key by provider_id taken verbatim from the QR header. Provider ids are fully-qualified as "<name>@<region>#<idx>", where <idx> is the 0-based position of the node inside its region in the provider-config. provider_keys.yaml is keyed in the exact same shape.

func (*Registry) Verify

func (r *Registry) Verify(providerID, upstreamID string, nonce uint64, signature, message []byte) error

Verify is a low-level helper mainly useful in tests: callers usually go through VerifyHeaders which handles header parsing and the source vs provider-id split. `upstreamID` is the string dshackle fed to its signer as `source` — i.e. what's in parens in the QR header prefix.

func (*Registry) VerifyHeaders

func (r *Registry) VerifyHeaders(headers http.Header, result []byte, expectedRequestID string, minRequired int) error

VerifyHeaders requires `result` to be the exact raw JSON-RPC "result" bytes the provider signed. `minRequired` is the client-supplied `quorum_required=` value; fewer valid signatures than this is rejected. When `expectedRequestID` is non-empty it is cross-checked against the id encoded in every QR header to reject cross-request signature replays. Returns nil only when every QR header is present, matches the expected request id, and verifies against the provider's public key.

type Signature

type Signature struct {
	Index      int
	RequestID  string
	ProviderID string // key in provider_keys.yaml (header prefix before the optional "(...)" )
	UpstreamID string // dshackle `source` used for signing (content inside parens, falls back to ProviderID)
	Nonce      uint64
	Raw        []byte
}

func ExtractSignatures

func ExtractSignatures(headers http.Header) ([]Signature, error)

type UnexpectedRequestIDError

type UnexpectedRequestIDError struct {
	Expected string
	Got      string
	Index    int
}

UnexpectedRequestIDError is returned when the request-id embedded in the QR header name does not match the actual JSON-RPC request id the gateway sent. This prevents an upstream from replaying signatures across requests.

func (*UnexpectedRequestIDError) Error

func (e *UnexpectedRequestIDError) Error() string

func (*UnexpectedRequestIDError) Is

func (e *UnexpectedRequestIDError) Is(target error) bool

type UnknownProviderError

type UnknownProviderError struct {
	ProviderID string
	Index      int
}

UnknownProviderError is returned when a QR header references a provider that is not present in the local keys registry.

func (*UnknownProviderError) Error

func (e *UnknownProviderError) Error() string

func (*UnknownProviderError) Is

func (e *UnknownProviderError) Is(target error) bool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL