Update golangci-lint to v2.12.2 with canonical config
All checks were successful
check / check (push) Successful in 1m8s

- Replace .golangci.yml with the canonical strict config (all linters
  enabled except the standard disable list; lll 88, funlen 80/50,
  cyclop 15, dupl 100; test files now linted)
- Pin the Dockerfile lint stage to golangci/golangci-lint:v2.12.2 by
  tag and digest (Debian-based)
- Fix all ~1550 findings surfaced by the new config: line wrapping,
  wsl_v5/nlreturn blank lines, noinlineerr splits, err113 sentinel
  errors, perfsprint/modernize rewrites, goconst constants, thelper,
  testifylint, noctx CommandContext, testpackage conversions,
  t.Parallel() where safe, and complexity/dupl helper extraction
- Record the change and follow-up items in TODO.md
This commit is contained in:
2026-08-07 17:27:23 +00:00
parent 6e5e0db999
commit 9ee216f629
59 changed files with 6568 additions and 4890 deletions

View File

@@ -9,6 +9,7 @@ import (
"encoding/base64"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
@@ -23,10 +24,10 @@ import (
const (
// BIP85_MASTER_PATH is the derivation path prefix for all BIP85 applications
BIP85_MASTER_PATH = "m/83696968'" //nolint:revive // ALL_CAPS used for BIP85 constants
BIP85_MASTER_PATH = "m/83696968'" //nolint:revive // BIP85 spec naming
// BIP85_KEY_HMAC_KEY is the HMAC key used for deriving the entropy
BIP85_KEY_HMAC_KEY = "bip-entropy-from-k" //nolint:revive // ALL_CAPS used for BIP85 constants
BIP85_KEY_HMAC_KEY = "bip-entropy-from-k" //nolint:revive // BIP85 spec naming
// AppBIP39 is the application number for BIP39 mnemonics
AppBIP39 = 39
@@ -34,18 +35,43 @@ const (
AppHDWIF = 2
// AppXPRV is the application number for extended private key
AppXPRV = 32
APP_HEX = 128169 //nolint:revive // ALL_CAPS used for BIP85 constants
APP_PWD64 = 707764 // Base64 passwords //nolint:revive // ALL_CAPS used for BIP85 constants
APP_HEX = 128169 //nolint:revive // BIP85 spec naming
APP_PWD64 = 707764 // Base64 passwords //nolint:revive // BIP85 spec naming
AppPWD85 = 707785 // Base85 passwords
APP_RSA = 828365 //nolint:revive // ALL_CAPS used for BIP85 constants
APP_RSA = 828365 //nolint:revive // BIP85 spec naming
)
// Sentinel errors for BIP85 derivation.
var (
// ErrNotPrivateKey is returned when the supplied master key is not a
// private key.
ErrNotPrivateKey = errors.New("master key must be a private key")
// ErrInvalidPathComponent is returned when a derivation path component
// cannot be parsed.
ErrInvalidPathComponent = errors.New("invalid path component")
// ErrInvalidWordCount is returned for unsupported BIP39 word counts.
ErrInvalidWordCount = errors.New("invalid BIP39 word count")
// ErrInvalidNumBytes is returned when numBytes is out of range.
ErrInvalidNumBytes = errors.New("numBytes must be between 16 and 64")
// ErrInvalidBase64PwdLen is returned when the Base64 password length
// is out of range.
ErrInvalidBase64PwdLen = errors.New("pwdLen must be between 20 and 86")
// ErrInvalidBase85PwdLen is returned when the Base85 password length
// is out of range.
ErrInvalidBase85PwdLen = errors.New("pwdLen must be between 10 and 80")
// ErrPasswordTooShort is returned when the derived material is
// shorter than the requested password length.
ErrPasswordTooShort = errors.New("derived password too short")
)
// Version bytes for extended keys
//
//nolint:gochecknoglobals // standard BIP32 version constants
var (
// MainNetPrivateKey is the version for mainnet private keys
MainNetPrivateKey = []byte{0x04, 0x88, 0xAD, 0xE4} //nolint:gochecknoglobals // Standard BIP32 constant
MainNetPrivateKey = []byte{0x04, 0x88, 0xAD, 0xE4}
// TestNetPrivateKey is the version for testnet private keys
TestNetPrivateKey = []byte{0x04, 0x35, 0x83, 0x94} //nolint:gochecknoglobals // Standard BIP32 constant
TestNetPrivateKey = []byte{0x04, 0x35, 0x83, 0x94}
)
// DRNG is a deterministic random number generator seeded by BIP85 entropy
@@ -71,7 +97,7 @@ func NewBIP85DRNG(entropy []byte) *DRNG {
}
// Read implements the io.Reader interface
func (d *DRNG) Read(p []byte) (n int, err error) {
func (d *DRNG) Read(p []byte) (int, error) {
return d.shake.Read(p)
}
@@ -79,7 +105,7 @@ func (d *DRNG) Read(p []byte) (n int, err error) {
func DeriveChildKey(masterKey *hdkeychain.ExtendedKey, path string) ([]byte, error) {
// Validate the masterKey is a private key
if !masterKey.IsPrivate() {
return nil, fmt.Errorf("master key must be a private key")
return nil, ErrNotPrivateKey
}
// Derive the child key at the specified path
@@ -98,8 +124,12 @@ func DeriveChildKey(masterKey *hdkeychain.ExtendedKey, path string) ([]byte, err
return ecPrivKey.Serialize(), nil
}
// DeriveBIP85Entropy derives entropy from a BIP32 master key using the BIP85 method
func DeriveBIP85Entropy(masterKey *hdkeychain.ExtendedKey, path string) ([]byte, error) {
// DeriveBIP85Entropy derives entropy from a BIP32 master key using the
// BIP85 method
func DeriveBIP85Entropy(
masterKey *hdkeychain.ExtendedKey,
path string,
) ([]byte, error) {
// Get the child key bytes
privKeyBytes, err := DeriveChildKey(masterKey, path)
if err != nil {
@@ -115,7 +145,10 @@ func DeriveBIP85Entropy(masterKey *hdkeychain.ExtendedKey, path string) ([]byte,
}
// deriveChildKey derives a child key from a parent key using the given path
func deriveChildKey(parent *hdkeychain.ExtendedKey, path string) (*hdkeychain.ExtendedKey, error) {
func deriveChildKey(
parent *hdkeychain.ExtendedKey,
path string,
) (*hdkeychain.ExtendedKey, error) {
if path == "" || path == "m" || path == "/" {
return parent, nil
}
@@ -141,9 +174,12 @@ func deriveChildKey(parent *hdkeychain.ExtendedKey, path string) (*hdkeychain.Ex
// Parse the index
var index uint32
_, err := fmt.Sscanf(component, "%d", &index)
if err != nil {
return nil, fmt.Errorf("invalid path component: %s", component)
return nil, fmt.Errorf(
"%w: %s", ErrInvalidPathComponent, component,
)
}
// Apply hardening if needed
@@ -164,8 +200,14 @@ func deriveChildKey(parent *hdkeychain.ExtendedKey, path string) (*hdkeychain.Ex
}
// DeriveBIP39Entropy derives entropy for a BIP39 mnemonic
func DeriveBIP39Entropy(masterKey *hdkeychain.ExtendedKey, language, words, index uint32) ([]byte, error) {
path := fmt.Sprintf("%s/%d'/%d'/%d'/%d'", BIP85_MASTER_PATH, AppBIP39, language, words, index)
func DeriveBIP39Entropy(
masterKey *hdkeychain.ExtendedKey,
language, words, index uint32,
) ([]byte, error) {
path := fmt.Sprintf(
"%s/%d'/%d'/%d'/%d'",
BIP85_MASTER_PATH, AppBIP39, language, words, index,
)
entropy, err := DeriveBIP85Entropy(masterKey, path)
if err != nil {
@@ -183,6 +225,7 @@ func DeriveBIP39Entropy(masterKey *hdkeychain.ExtendedKey, language, words, inde
)
var bits int
switch words {
case words12:
bits = 128
@@ -195,7 +238,7 @@ func DeriveBIP39Entropy(masterKey *hdkeychain.ExtendedKey, language, words, inde
case words24:
bits = 256
default:
return nil, fmt.Errorf("invalid BIP39 word count: %d", words)
return nil, fmt.Errorf("%w: %d", ErrInvalidWordCount, words)
}
// Truncate to the required number of bits (bytes = bits / 8)
@@ -218,6 +261,7 @@ func DeriveWIFKey(masterKey *hdkeychain.ExtendedKey, index uint32) (string, erro
// Convert to WIF format
privKey, _ := btcec.PrivKeyFromBytes(keyBytes)
wif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, true) // compressed=true
if err != nil {
return "", fmt.Errorf("failed to create WIF: %w", err)
@@ -227,7 +271,10 @@ func DeriveWIFKey(masterKey *hdkeychain.ExtendedKey, index uint32) (string, erro
}
// DeriveXPRV derives an extended private key (XPRV)
func DeriveXPRV(masterKey *hdkeychain.ExtendedKey, index uint32) (*hdkeychain.ExtendedKey, error) {
func DeriveXPRV(
masterKey *hdkeychain.ExtendedKey,
index uint32,
) (*hdkeychain.ExtendedKey, error) {
path := fmt.Sprintf("%s/%d'/%d'", BIP85_MASTER_PATH, AppXPRV, index)
entropy, err := DeriveBIP85Entropy(masterKey, path)
@@ -266,10 +313,10 @@ func DeriveXPRV(masterKey *hdkeychain.ExtendedKey, index uint32) (*hdkeychain.Ex
checksum := doubleSHA256(serializedBytes)[:4]
// Append checksum
serializedWithChecksum := append(serializedBytes, checksum...)
serializedBytes = append(serializedBytes, checksum...)
// Base58 encode
xprvStr := base58.Encode(serializedWithChecksum)
xprvStr := base58.Encode(serializedBytes)
// Parse the serialized xprv back to an ExtendedKey
return hdkeychain.NewKeyFromString(xprvStr)
@@ -284,9 +331,12 @@ func doubleSHA256(data []byte) []byte {
}
// DeriveHex derives a raw hex string of specified length
func DeriveHex(masterKey *hdkeychain.ExtendedKey, numBytes, index uint32) (string, error) {
func DeriveHex(
masterKey *hdkeychain.ExtendedKey,
numBytes, index uint32,
) (string, error) {
if numBytes < 16 || numBytes > 64 {
return "", fmt.Errorf("numBytes must be between 16 and 64")
return "", ErrInvalidNumBytes
}
path := fmt.Sprintf("%s/%d'/%d'/%d'", BIP85_MASTER_PATH, APP_HEX, numBytes, index)
@@ -303,9 +353,12 @@ func DeriveHex(masterKey *hdkeychain.ExtendedKey, numBytes, index uint32) (strin
}
// DeriveBase64Password derives a password encoded in Base64
func DeriveBase64Password(masterKey *hdkeychain.ExtendedKey, pwdLen, index uint32) (string, error) {
func DeriveBase64Password(
masterKey *hdkeychain.ExtendedKey,
pwdLen, index uint32,
) (string, error) {
if pwdLen < 20 || pwdLen > 86 {
return "", fmt.Errorf("pwdLen must be between 20 and 86")
return "", ErrInvalidBase64PwdLen
}
path := fmt.Sprintf("%s/%d'/%d'/%d'", BIP85_MASTER_PATH, APP_PWD64, pwdLen, index)
@@ -323,16 +376,22 @@ func DeriveBase64Password(masterKey *hdkeychain.ExtendedKey, pwdLen, index uint3
// Slice to the desired password length
if len(encodedStr) < int(pwdLen) {
return "", fmt.Errorf("derived password length %d is shorter than requested length %d", len(encodedStr), pwdLen)
return "", fmt.Errorf(
"%w: derived length %d is shorter than requested length %d",
ErrPasswordTooShort, len(encodedStr), pwdLen,
)
}
return encodedStr[:pwdLen], nil
}
// DeriveBase85Password derives a password encoded in Base85
func DeriveBase85Password(masterKey *hdkeychain.ExtendedKey, pwdLen, index uint32) (string, error) {
func DeriveBase85Password(
masterKey *hdkeychain.ExtendedKey,
pwdLen, index uint32,
) (string, error) {
if pwdLen < 10 || pwdLen > 80 {
return "", fmt.Errorf("pwdLen must be between 10 and 80")
return "", ErrInvalidBase85PwdLen
}
path := fmt.Sprintf("%s/%d'/%d'/%d'", BIP85_MASTER_PATH, AppPWD85, pwdLen, index)
@@ -347,16 +406,21 @@ func DeriveBase85Password(masterKey *hdkeychain.ExtendedKey, pwdLen, index uint3
// Slice to the desired password length
if len(encoded) < int(pwdLen) {
return "", fmt.Errorf("encoded length %d is less than requested length %d", len(encoded), pwdLen)
return "", fmt.Errorf(
"%w: encoded length %d is less than requested length %d",
ErrPasswordTooShort, len(encoded), pwdLen,
)
}
return encoded[:pwdLen], nil
}
// encodeBase85WithRFC1924Charset encodes data using Base85 with the RFC1924 character set
// encodeBase85WithRFC1924Charset encodes data using Base85 with the
// RFC1924 character set
func encodeBase85WithRFC1924Charset(data []byte) string {
// RFC1924 character set
charset := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
charset := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
const (
base85ChunkSize = 4 // Process 4 bytes at a time
@@ -369,7 +433,9 @@ func encodeBase85WithRFC1924Charset(data []byte) string {
copy(padded, data)
var buf strings.Builder
buf.Grow(len(padded) * base85DigitCount / base85ChunkSize) // Each 4 bytes becomes 5 Base85 characters
// Each 4 bytes becomes 5 Base85 characters
buf.Grow(len(padded) * base85DigitCount / base85ChunkSize)
// Process in 4-byte chunks
for i := 0; i < len(padded); i += base85ChunkSize {