Files
kms/vendor/github.com/luxfi/formatting/encoding.go
T
zeekayandHanzo Dev 1c8b627a25 build: resync vendor/ so the kms-operator image can build again
ghcr.io/luxfi/kms-operator has had no new image since v1.12.3. The Build KMS
Operator workflow failed on every tag since (v1.12.4 … v1.12.7) while its
sibling Build KMS succeeded on the same tag, so the tags looked published.

Dockerfile.operator:27 builds with -mod=vendor, and vendor/ had drifted from
go.mod in both directions:
  - go.opentelemetry.io/otel/{metric,trace}: in vendor/modules.txt, not in go.mod
  - github.com/luxfi/{crypto,geth,ids,keys,zap,address,age,cache,constants,
    container,formatting,math,go-bip32,go-bip39}: in go.mod, not marked explicit

`go mod vendor` alone could not fix it — it aborted first on go.sum entries
missing for the k8s packages cmd/kms-operator imports (k8s.io/api/core/v1,
k8s.io/apimachinery/..., k8s.io/client-go/{dynamic,kubernetes,rest}). So:
go mod tidy (adds them, drops now-unused indirects) then go mod vendor.

Verified by stash control:
  operator, -mod=vendor:  rc=1 -> rc=0   ("inconsistent vendoring" 1 -> 0)
  server,   -mod=vendor:  rc=1 -> rc=1   (unchanged)

The server's vendor build was already broken and stays broken, for an unrelated
reason: blst.h has never been vendored (confirmed absent from HEAD's tree, so it
could not have worked before either) — go mod vendor does not copy cgo headers
that live outside the package dir. It is not a regression and does not affect
CI: the server's Dockerfile builds with GOFLAGS=-mod=mod after `rm -f go.sum &&
go mod download`, i.e. from the module cache, never from vendor/. The operator
is the only -mod=vendor consumer, which is exactly why it was the only failure.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-25 17:39:26 -07:00

173 lines
4.0 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package formatting
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"math"
"strings"
"github.com/luxfi/crypto/hash"
)
const (
hexPrefix = "0x"
checksumLen = 4
)
var (
errEncodingOverFlow = errors.New("encoding overflow")
errInvalidEncoding = errors.New("invalid encoding")
errUnsupportedEncodingInMethod = errors.New("unsupported encoding in method")
errMissingChecksum = errors.New("input string is smaller than the checksum size")
errBadChecksum = errors.New("invalid input checksum")
errMissingHexPrefix = errors.New("missing 0x prefix to hex encoding")
)
// Encoding defines how bytes are converted to a string and vice versa
type Encoding uint8
const (
// Hex specifies a hex plus 4 byte checksum encoding format
Hex Encoding = iota
// HexNC specifies a hex encoding format
HexNC
// HexC specifies a hex plus 4 byte checksum encoding format
HexC
// JSON specifies the JSON encoding format
JSON
)
func (enc Encoding) String() string {
switch enc {
case Hex:
return "hex"
case HexNC:
return "hexnc"
case HexC:
return "hexc"
case JSON:
return "json"
default:
return errInvalidEncoding.Error()
}
}
func (enc Encoding) valid() bool {
switch enc {
case Hex, HexNC, HexC, JSON:
return true
}
return false
}
func (enc Encoding) MarshalJSON() ([]byte, error) {
if !enc.valid() {
return nil, errInvalidEncoding
}
return []byte(`"` + enc.String() + `"`), nil
}
func (enc *Encoding) UnmarshalJSON(b []byte) error {
str := string(b)
if str == "null" {
return nil
}
switch strings.ToLower(str) {
case `"hex"`:
*enc = Hex
case `"hexnc"`:
*enc = HexNC
case `"hexc"`:
*enc = HexC
case `"json"`:
*enc = JSON
default:
return errInvalidEncoding
}
return nil
}
// Encode [bytes] to a string using the given encoding format [bytes] may be
// nil, in which case it will be treated the same as an empty slice.
func Encode(encoding Encoding, bytes []byte) (string, error) {
if !encoding.valid() {
return "", errInvalidEncoding
}
switch encoding {
case Hex, HexC:
bytesLen := len(bytes)
if bytesLen > math.MaxInt32-checksumLen {
return "", errEncodingOverFlow
}
checked := make([]byte, bytesLen+checksumLen)
copy(checked, bytes)
copy(checked[len(bytes):], hash.Checksum(bytes, checksumLen))
bytes = checked
}
switch encoding {
case Hex, HexNC, HexC:
return fmt.Sprintf("0x%x", bytes), nil
case JSON:
// JSON Marshal does not support []byte input and we rely on the
// router's json marshalling to marshal our interface{} into JSON
// in response. Therefore it is not supported in this call.
return "", errUnsupportedEncodingInMethod
default:
return "", errInvalidEncoding
}
}
// Decode [str] to bytes using the given encoding
// If [str] is the empty string, returns a nil byte slice and nil error
func Decode(encoding Encoding, str string) ([]byte, error) {
switch {
case !encoding.valid():
return nil, errInvalidEncoding
case len(str) == 0:
return nil, nil
}
var (
decodedBytes []byte
err error
)
switch encoding {
case Hex, HexNC, HexC:
if !strings.HasPrefix(str, hexPrefix) {
return nil, errMissingHexPrefix
}
decodedBytes, err = hex.DecodeString(str[2:])
case JSON:
// JSON unmarshalling requires interface and has no return values
// contrary to this method, therefore it is not supported in this call
return nil, errUnsupportedEncodingInMethod
default:
return nil, errInvalidEncoding
}
if err != nil {
return nil, err
}
switch encoding {
case Hex, HexC:
if len(decodedBytes) < checksumLen {
return nil, errMissingChecksum
}
// Verify the checksum
rawBytes := decodedBytes[:len(decodedBytes)-checksumLen]
checksum := decodedBytes[len(decodedBytes)-checksumLen:]
if !bytes.Equal(checksum, hash.Checksum(rawBytes, checksumLen)) {
return nil, errBadChecksum
}
decodedBytes = rawBytes
}
return decodedBytes, nil
}