fix(deps): post-re-tag coherent luxfi set (geth v1.17.12), re-vendor

luxfi/geth wiped its v1.16.x line (v1.16.98 gone) in the ecosystem
re-tag, breaking 'go mod download' at Dockerfile:27. Move to the coherent
set crypto v1.19.27 -> geth v1.17.12 (both proxy-resolvable), regenerate
go.sum against the public proxy (matches the Dockerfile's proxy fetch),
and re-vendor so the in-tree vendor/ stays consistent. Pairs with the
already-landed KMS<->MPC ZAP wire fix (7377ad6) so a tagged release both
builds and signs against mpcd.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
zeekay
2026-07-14 16:26:04 -07:00
co-authored by Hanzo Dev
parent 7377ad63d2
commit e361c1258d
64 changed files with 2905 additions and 1468 deletions
+18 -12
View File
@@ -8,7 +8,7 @@ require (
github.com/luxfi/go-bip39 v1.1.2
github.com/luxfi/ids v1.2.15
github.com/luxfi/keys v1.1.0
github.com/luxfi/log v1.4.1
github.com/luxfi/log v1.4.3
github.com/luxfi/zap v0.7.2
github.com/luxfi/zapdb v1.10.0
golang.org/x/crypto v0.52.0
@@ -19,14 +19,18 @@ require (
require (
filippo.io/hpke v0.4.0 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/btcsuite/btcd/btcutil v1.1.6 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/consensys/gnark-crypto v0.20.1 // indirect
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
github.com/dgraph-io/ristretto/v2 v2.4.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.7 // indirect
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
@@ -43,38 +47,40 @@ require (
github.com/holiman/uint256 v1.3.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/luxfi/accel v1.1.9 // indirect
github.com/luxfi/accel v1.2.4 // indirect
github.com/luxfi/address v1.0.1 // indirect
github.com/luxfi/age v1.5.0 // indirect
github.com/luxfi/cache v1.2.1 // indirect
github.com/luxfi/constants v1.5.8 // indirect
github.com/luxfi/container v0.0.4 // indirect
github.com/luxfi/crypto v1.19.17 // indirect
github.com/luxfi/crypto v1.19.27 // indirect
github.com/luxfi/crypto/ipa v1.2.4 // indirect
github.com/luxfi/formatting v1.0.1 // indirect
github.com/luxfi/geth v1.16.98 // indirect
github.com/luxfi/geth v1.17.12 // indirect
github.com/luxfi/go-bip32 v1.0.2 // indirect
github.com/luxfi/math v1.4.1 // indirect
github.com/luxfi/math/big v0.1.0 // indirect
github.com/luxfi/mdns v0.1.1 // indirect
github.com/luxfi/metric v1.5.7 // indirect
github.com/luxfi/metric v1.5.8 // indirect
github.com/luxfi/mock v0.1.1 // indirect
github.com/luxfi/proto v1.0.0 // indirect
github.com/luxfi/pq v1.0.3 // indirect
github.com/luxfi/proto v1.3.4 // indirect
github.com/luxfi/sampler v1.1.0 // indirect
github.com/luxfi/tls v1.0.3 // indirect
github.com/luxfi/vm v1.2.0 // indirect
github.com/luxfi/vm v1.2.3 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/miekg/dns v1.1.72 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.100 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/mr-tron/base58 v1.3.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo/v2 v2.28.1 // indirect
github.com/onsi/gomega v1.39.1 // indirect
@@ -93,7 +99,7 @@ require (
go.uber.org/mock v0.6.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9 // indirect
golang.org/x/mod v0.36.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
+36 -54
View File
@@ -1,10 +1,8 @@
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd h1:ZLsPO6WdZ5zatV4UfVpr7oAwLGRZ+sebTUruuM4Ra3M=
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo=
filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A=
filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
@@ -34,6 +32,10 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/consensys/gnark-crypto v0.20.1 h1:PXDUBvk8AzhvWowHLWBEAfUQcV1/aZgWIqD6eMpXmDg=
github.com/consensys/gnark-crypto v0.20.1/go.mod h1:RBWrSgy+IDbGR69RRV313th3M/aZU1ubk2om+qHuTSc=
github.com/crate-crypto/go-eth-kzg v1.5.0 h1:FYRiJMJG2iv+2Dy3fi14SVGjcPteZ5HAAUe4YWlJygc=
github.com/crate-crypto/go-eth-kzg v1.5.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -41,20 +43,18 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU=
github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/ethereum/c-kzg-4844/v2 v2.1.7 h1:aat3CuITdDbPC6pmEGRT0zJ5eOxzrZj8TJT5z7Xk//M=
github.com/ethereum/c-kzg-4844/v2 v2.1.7/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ=
@@ -76,8 +76,6 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -95,11 +93,7 @@ github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7O
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw=
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/rpc v1.2.1 h1:yC+LMV5esttgpVvNORL/xX4jvTTEUE30UZhZ5JF7K9k=
@@ -120,22 +114,19 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/luxfi/accel v1.1.9 h1:Tsk6gXj2uKE19501bD0ajRYdeCHIlTGb6jYyLc+F8hc=
github.com/luxfi/accel v1.1.9/go.mod h1:K00BcnLzEYMPHwCFq8Tf/450ApmTs9xBVvYOnofJCkc=
github.com/luxfi/accel v1.2.4 h1:5VbIHyEvvfobn2zBiTFODxDw1CeqxCepZOLlvkuf9yQ=
github.com/luxfi/accel v1.2.4/go.mod h1:ISIwAX+ZfsL/S5nsP2JvfldXN6Nc+QzoWf6Jtaq+xsQ=
github.com/luxfi/address v1.0.1 h1:Sc4keyuVzBIvHr7uVeYZf2/WY9YDGUgDi/iiWenj49g=
github.com/luxfi/address v1.0.1/go.mod h1:5j3Eh66v9zvv1GbNdZwt+23krV8JlSDaRzmWZU8ZRM0=
github.com/luxfi/age v1.5.0 h1:G69HbSV4R3vKEH9B0CulnRaMdSdf4RalMgP8xKmxHeI=
@@ -146,52 +137,54 @@ github.com/luxfi/constants v1.5.8 h1:iNP9AWNUcM4Tps7jYnx49CwtCWAC9mYRxJfGou2za0g
github.com/luxfi/constants v1.5.8/go.mod h1:Pu5jWHdnUtQRbWC43yTUjU/pbIIKMDOd2a2yroSfo48=
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
github.com/luxfi/crypto v1.19.17 h1:l2LLu7UFyICtJVfraLDLRi+lFGiDXKHSL18M9/m1gsQ=
github.com/luxfi/crypto v1.19.17/go.mod h1:INjdZtke85k8hX/QAmTMAY8bbZ4gzGZQLqURg3xf6Gk=
github.com/luxfi/crypto v1.19.27 h1:K4l9nsnB0t61zHO5u8si06zVzW5WTjWW+/6+KRb/bfo=
github.com/luxfi/crypto v1.19.27/go.mod h1:0DCU62kX8+zhYU2qeM07A4pifJyPkPujnUOfgc8TOFQ=
github.com/luxfi/crypto/ipa v1.2.4 h1:6xfwhI9/HrcDkF3Ti5/NxsNQIWbwYDJmRSNIHRQ/xfU=
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEYo=
github.com/luxfi/formatting v1.0.1/go.mod h1:mYzNf5DJOiqSSKUPzNj5dKy4tstFbN3pZlkI5716eKc=
github.com/luxfi/geth v1.16.98 h1:w187TtKuGStf3tm2bshuHVKBv2Frjx0lT54kQVXyNHA=
github.com/luxfi/geth v1.16.98/go.mod h1:6kEzSExdk9CPQDPXALt6P3HfQqBq7KF1Jrrv9gBpxbU=
github.com/luxfi/geth v1.17.12 h1:UP/fhpcfbGPTrkOCwX3d88Oc3jVm5gTOgfjgq+lek6s=
github.com/luxfi/geth v1.17.12/go.mod h1:3vQfQJd9JC+AVBjxNXa9PYQOqpbE2dKu8E3jqhPZ3LU=
github.com/luxfi/go-bip32 v1.0.2 h1:7vFbb+Wr4Z499q2tuCLdd7wWjtn8sH+HWBlx76mhH9Y=
github.com/luxfi/go-bip32 v1.0.2/go.mod h1:bc7/LXDKAJQZ/F0Xjf5yXaTZxY9/ssLb4FC+Hxn/cDk=
github.com/luxfi/go-bip39 v1.1.2 h1:p+wLMPGs6MLQh7q0YIsmy2EhHL7LHiELEGTJko6t/Jg=
github.com/luxfi/go-bip39 v1.1.2/go.mod h1:96de9VkR2kY/ASAnhMtvt3TSh+PZkAFAngNj0GjRGDo=
github.com/luxfi/ids v1.2.15 h1:omE+E4+0Poj9DzM11ejSFgteaSQ3KDHi5g54iH6jcxI=
github.com/luxfi/ids v1.2.15/go.mod h1:Fj73K5xcblvdE0SxU/ip+jE8VqNdu+80548su5KJ7xI=
github.com/luxfi/keys v1.1.0 h1:a4UkVVg6G09XC7vPtXKxEGwVt50GNPjEvq2pkjYZW2k=
github.com/luxfi/keys v1.1.0 h1:/3mQflSCTMy/bTGbYXh2xJ+NhYDKRoBMimYpj2/d3XM=
github.com/luxfi/keys v1.1.0/go.mod h1:U3tZNDmv3nXkPoZwLtq9RNjwyN0XyoN29worigfT+c0=
github.com/luxfi/log v1.4.1 h1:rIfFRodb9jrD/w7KayaUk0Oc+37PaQQdKEEMJCjR8gw=
github.com/luxfi/log v1.4.1/go.mod h1:64IE3xRMJcpkQwnPUfJw3pDj7wU0kRS7BZ9wM7R72jk=
github.com/luxfi/log v1.4.3 h1:xkUKRWvQ4ZwvlUC2e0/RTtHYZOYSMvSQ9W9lbjwBmiI=
github.com/luxfi/log v1.4.3/go.mod h1:myIkufyiQomSQH34K981kbz6cG4WUoerRUh7F4XhlQI=
github.com/luxfi/math v1.4.1 h1:1t9bCCsEqnl9yIKrShlbs80DBKyYTWdnzkVfBqEeO7Q=
github.com/luxfi/math v1.4.1/go.mod h1:QvbRxauQyE1w4lvbcLSe6c8yeJz2Zj1Bq1rayGgs2tA=
github.com/luxfi/math/big v0.1.0 h1:Vz4c0RsZVPdIKPsHPgAJChH/R3p15WHRUz7LkLf+NIQ=
github.com/luxfi/math/big v0.1.0/go.mod h1:BuxSu22RbO93xBLk5Eam5nldFponoJ73xDFz4uJ3Huk=
github.com/luxfi/mdns v0.1.1 h1:g2eRr9AXcziPkkcd24M+Qu9ApEpoKKjfI79QSNqv0rQ=
github.com/luxfi/mdns v0.1.1/go.mod h1:dbp5f3h3aE7CGzwbaWzBM9cwdcekhmSrWhQevgYhhNA=
github.com/luxfi/metric v1.5.7 h1:LoSPEUpak2SLcynF+LT2cXjl9ECp4nY+Lia9zudmDv4=
github.com/luxfi/metric v1.5.7/go.mod h1:CMguEhyuLi4YUWyXimJ+UHply99BDFrL0pxedB7rBqM=
github.com/luxfi/metric v1.5.8 h1:axPwfq+erOlIue7IJp5g+hMcMtVhYHja9gJAaT3+KNA=
github.com/luxfi/metric v1.5.8/go.mod h1:fO2giazkg4NDtr72JM/QXJBYebplAMeWC1JoZyNDvKw=
github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
github.com/luxfi/pq v1.0.3 h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs=
github.com/luxfi/pq v1.0.3 h1:ksw1dmfTR0dqqNMRS7BjGcprCO2Fhc+3Iiq2/NMuONw=
github.com/luxfi/pq v1.0.3/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
github.com/luxfi/proto v1.0.0 h1:nlxv4lt/i75XDB2Q3nTqC1o0RDPmJ9K9CJ8UyYvceak=
github.com/luxfi/proto v1.0.0/go.mod h1:pZLKsCmhiPtmm3z7ezBbnsYT2m79q+cFSmzxvuBeANE=
github.com/luxfi/proto v1.3.4 h1:dNrdQ/414Pe9otIekVUmQ9QLqemaMFK43H4N3DIbuzQ=
github.com/luxfi/proto v1.3.4/go.mod h1:S80KrTzISCktptit/pWLzk6nHgagjJS86qbQL6gHutE=
github.com/luxfi/sampler v1.1.0 h1:u3iRDl7V06ARh0e85h3HT+aZ1saCFo2yMMsh+dCJbqk=
github.com/luxfi/sampler v1.1.0/go.mod h1:kJa53S3tC9+VSbuV3RFu68MmbCCBlr2UM39LOClQ/Hs=
github.com/luxfi/tls v1.0.3 h1:rK3nxSAxrUOOSHOZnKChwV4f6UJ+cfOl8KWJXAQx/SI=
github.com/luxfi/tls v1.0.3/go.mod h1:dQqSiGE7YxXUxOwICoReUuIitBms9DYOaCeteBwmIWw=
github.com/luxfi/vm v1.2.0 h1:jTwQRHdC9VmyRZTPSn+IqjMju7f6xlxLc6P9CEg+Y2M=
github.com/luxfi/vm v1.2.0/go.mod h1:qasVIBRerVQuvy9vFGrX3H8X8pPMPG5un/KbZSyq5YY=
github.com/luxfi/vm v1.2.3 h1:IdfB02OtDmdy/YNxaIR8DquPp5Aq0fi5QXBqGHoRkug=
github.com/luxfi/vm v1.2.3/go.mod h1:ZXmw1sKA6GL3Ma3BfYUv5Fzvx3aWu2QMtkxF3lL/3R4=
github.com/luxfi/zap v0.7.2 h1:YecWTWNE5PPJXL56sLIkzS8b23bprUwZ5lPAQuLUtTE=
github.com/luxfi/zap v0.7.2/go.mod h1:1k+nwT+JW802YzuPAuf7CxMSGr/qxvbGgGwi5k6X9Ok=
github.com/luxfi/zapdb v1.10.0 h1:1lLHEmkyC0BucnA/zjQYsMkUVxuEo2vQkEaQGjYfuuc=
github.com/luxfi/zapdb v1.10.0/go.mod h1:Qukh3hDRD0MnxA6z+a28JTnXhN85AiLLgp6TYr4QAMc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
@@ -207,8 +200,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.3.0 h1:K6Y13R2h+dku0wOqKtecgRnBUBPrZzLZy5aIj8lCcJI=
github.com/mr-tron/base58 v1.3.0/go.mod h1:2BuubE67DCSWwVfx37JWNG8emOC0sHEU4/HpcYgCLX8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -216,23 +209,17 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
@@ -240,7 +227,6 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -248,8 +234,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
@@ -279,8 +263,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9 h1:4d4PbuBNwaxMXkXI8yiIYjydtMU+04RHeuSxJdgKftM=
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -317,7 +301,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
@@ -351,7 +334,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+1
View File
@@ -1,2 +1,3 @@
* -text
*.bin -text -diff
*.md text eol=lf
+700 -700
View File
File diff suppressed because it is too large Load Diff
+78 -78
View File
@@ -1,79 +1,79 @@
# Finite State Entropy
This package provides Finite State Entropy encoding and decoding.
Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
encoding provides a fast near-optimal symbol encoding/decoding
for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
## News
* Feb 2018: First implementation released. Consider this beta software for now.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `(error)` | An internal error occurred. |
As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
# Performance
A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
All compression functions are currently only running on the calling goroutine so only one core will be used per block.
The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
beneficial to transpose all your input values down by 64.
With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
around 300MB/s decompression speed.
The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
# Plans
At one point, more internals will be exposed to facilitate more "expert" usage of the components.
A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
# Finite State Entropy
This package provides Finite State Entropy encoding and decoding.
Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
encoding provides a fast near-optimal symbol encoding/decoding
for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
## News
* Feb 2018: First implementation released. Consider this beta software for now.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `(error)` | An internal error occurred. |
As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
# Performance
A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
All compression functions are currently only running on the calling goroutine so only one core will be used per block.
The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
beneficial to transpose all your input values down by 64.
With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
around 300MB/s decompression speed.
The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
# Plans
At one point, more internals will be exposed to facilitate more "expert" usage of the components.
A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
+89 -89
View File
@@ -1,89 +1,89 @@
# Huff0 entropy compression
This package provides Huff0 encoding and decoding as used in zstd.
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
## News
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
This ensures that most functionality is well tested.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
| `(error)` | An internal error occurred. |
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
## Tables and re-use
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
that controls this behaviour. See the documentation for details. This can be altered between each block.
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
based on the boolean reported back from the CompressXX call.
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
## Decompressing
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
This will initialize the decoding tables.
You can supply the complete block to `ReadTable` and it will return the data part of the block
which can be given to the decompressor.
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
# Huff0 entropy compression
This package provides Huff0 encoding and decoding as used in zstd.
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
## News
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
This ensures that most functionality is well tested.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
| `(error)` | An internal error occurred. |
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
## Tables and re-use
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
that controls this behaviour. See the documentation for details. This can be altered between each block.
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
based on the boolean reported back from the CompressXX call.
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
## Decompressing
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
This will initialize the decoding tables.
You can supply the complete block to `ReadTable` and it will return the data part of the block
which can be given to the decompressor.
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
+1 -1
View File
@@ -51,7 +51,7 @@
//
// The d variable is implicitly R_DST - R_DBASE, and len(dst)-d is R_DEND - R_DST.
// The s variable is implicitly R_SRC - R_SBASE, and len(src)-s is R_SEND - R_SRC.
TEXT ·s2Decode(SB), NOSPLIT, $48-56
TEXT ·s2Decode(SB), NOSPLIT, $56-56
// Initialize R_SRC, R_DST and R_DBASE-R_SEND.
MOVQ dst_base+0(FP), R_DBASE
MOVQ dst_len+8(FP), R_DLEN
+16 -12
View File
@@ -23,12 +23,12 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
lTableBits = 19
maxLTableSize = 1 << lTableBits
lTableBits = bestLongTableBits
maxLTableSize = bestLongTableSize
// Short hash matches.
sTableBits = 16
maxSTableSize = 1 << sTableBits
sTableBits = bestShortTableBits
maxSTableSize = bestShortTableSize
inputMargin = 8 + 2
@@ -44,8 +44,10 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) {
}
sLimitDict := min(len(src)-inputMargin, MaxDictSrcOffset-inputMargin)
var lTable [maxLTableSize]uint64
var sTable [maxSTableSize]uint64
tbl := getBestTables()
lTable := &tbl.lTable
sTable := &tbl.sTable
defer bestTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - 5
@@ -456,12 +458,12 @@ func encodeBlockBestSnappy(dst, src []byte) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
lTableBits = 19
maxLTableSize = 1 << lTableBits
lTableBits = bestLongTableBits
maxLTableSize = bestLongTableSize
// Short hash matches.
sTableBits = 16
maxSTableSize = 1 << sTableBits
sTableBits = bestShortTableBits
maxSTableSize = bestShortTableSize
inputMargin = 8 + 2
)
@@ -474,8 +476,10 @@ func encodeBlockBestSnappy(dst, src []byte) (d int) {
return 0
}
var lTable [maxLTableSize]uint64
var sTable [maxSTableSize]uint64
tbl := getBestTables()
lTable := &tbl.lTable
sTable := &tbl.sTable
defer bestTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - 5
+24 -18
View File
@@ -59,16 +59,18 @@ func encodeBlockBetterGo(dst, src []byte) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
lTableBits = 17
maxLTableSize = 1 << lTableBits
lTableBits = betterLongTableBits
maxLTableSize = betterLongTableSize
// Short hash matches.
sTableBits = 14
maxSTableSize = 1 << sTableBits
sTableBits = betterShortTableBits
maxSTableSize = betterShortTableSize
)
var lTable [maxLTableSize]uint32
var sTable [maxSTableSize]uint32
tbl := getBetterTables()
lTable := &tbl.lTable
sTable := &tbl.sTable
defer betterTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - len(src)>>5 - 6
@@ -317,16 +319,18 @@ func encodeBlockBetterSnappyGo(dst, src []byte) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
lTableBits = 16
maxLTableSize = 1 << lTableBits
lTableBits = betterSnappyLongTableBits
maxLTableSize = betterSnappyLongTableSize
// Short hash matches.
sTableBits = 14
maxSTableSize = 1 << sTableBits
sTableBits = betterShortTableBits
maxSTableSize = betterShortTableSize
)
var lTable [maxLTableSize]uint32
var sTable [maxSTableSize]uint32
tbl := getBetterSnappyTables()
lTable := &tbl.lTable
sTable := &tbl.sTable
defer betterSnappyTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - len(src)>>5 - 6
@@ -902,12 +906,12 @@ func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
lTableBits = 17
maxLTableSize = 1 << lTableBits
lTableBits = betterLongTableBits
maxLTableSize = betterLongTableSize
// Short hash matches.
sTableBits = 14
maxSTableSize = 1 << sTableBits
sTableBits = betterShortTableBits
maxSTableSize = betterShortTableSize
maxAhead = 8 // maximum bytes ahead without checking sLimit
@@ -921,8 +925,10 @@ func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) {
dict.initBetter()
var lTable [maxLTableSize]uint32
var sTable [maxSTableSize]uint32
tbl := getBetterTables()
lTable := &tbl.lTable
sTable := &tbl.sTable
defer betterTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - len(src)>>5 - 6
+65
View File
@@ -0,0 +1,65 @@
package s2
import "sync"
// Table size constants
const (
betterLongTableBits = 17
betterLongTableSize = 1 << betterLongTableBits // 131072
betterShortTableBits = 14
betterShortTableSize = 1 << betterShortTableBits // 16384
betterSnappyLongTableBits = 16
betterSnappyLongTableSize = 1 << betterSnappyLongTableBits // 65536
bestLongTableBits = 19
bestLongTableSize = 1 << bestLongTableBits // 524288
bestShortTableBits = 16
bestShortTableSize = 1 << bestShortTableBits // 65536
)
type betterTables struct {
lTable [betterLongTableSize]uint32
sTable [betterShortTableSize]uint32
}
var betterTablePool = sync.Pool{New: func() interface{} { return &betterTables{} }}
// betterSnappyTables holds better-snappy compression hash tables.
type betterSnappyTables struct {
lTable [betterSnappyLongTableSize]uint32
sTable [betterShortTableSize]uint32
}
var betterSnappyTablePool = sync.Pool{New: func() interface{} { return &betterSnappyTables{} }}
// bestTables holds best compression hash tables.
type bestTables struct {
lTable [bestLongTableSize]uint64
sTable [bestShortTableSize]uint64
}
var bestTablePool = sync.Pool{New: func() interface{} { return &bestTables{} }}
// getBetterTables gets a zeroed betterTables from the pool.
func getBetterTables() *betterTables {
t := betterTablePool.Get().(*betterTables)
*t = betterTables{}
return t
}
// getBetterSnappyTables gets a zeroed betterSnappyTables from the pool.
func getBetterSnappyTables() *betterSnappyTables {
t := betterSnappyTablePool.Get().(*betterSnappyTables)
*t = betterSnappyTables{}
return t
}
// getBestTables gets a zeroed bestTables from the pool.
func getBestTables() *bestTables {
t := bestTablePool.Get().(*bestTables)
*t = bestTables{}
return t
}
+44
View File
@@ -75,6 +75,19 @@ lux/accel/
- Kyber encapsulation/decapsulation
- Dilithium signing/verification
- Polynomial NTT/iNTT
- ML-DSA batched verify / sign (FIPS 204; modes 2/3/5 = ML-DSA-44/65/87) —
routes through `LatticeOps.MLDSAVerifyBatch` / `MLDSASignBatch` (C ABI:
`lux_mldsa_verify_batch`, `lux_mldsa_sign_batch`). Stubs return
LUX_NO_BACKEND when libluxaccel has no impl; Go-side callers (Pulsar)
fall back to per-element CPU verify.
- `LatticeNTTMLDSABatch` — in-place forward / inverse NTT over
Z_q[X]/(X^256+1), q=8380417 (ML-DSA prime), batched across N polynomials.
C ABI `lux_lattice_ntt_mldsa_batch`. CPU oracle in
`ops/lattice/mldsa_ntt.go` is byte-equal to PQCLEAN_MLDSA65_CLEAN_ntt
(the same body GPU plugins ship under lux-private/gpu-kernels/ops/
lattice/ntt_mldsa/). `accel.MLDSABatchThreshold = 8` — below this the
Go top-level dispatch returns ErrNotSupported so consumers route to
the per-poly CPU path.
### DEX (`ops/dex`)
- Constant product swaps
@@ -220,6 +233,37 @@ go build -tags=accel ./...
# Enables GPU implementations in ops/*
```
### With lux_accel_real tag (link libluxaccel)
The capi layer at `internal/capi/` has TWO mutually-exclusive build
files. Selector: `lux_accel_real`.
| Build | File compiled | C symbols resolved by |
|---|---|---|
| `go build ./...` (default) | `stub_default.go` | in-tree stub bodies returning `LUX_NO_BACKEND` |
| `go build -tags lux_accel_real ./...` | `real.go` | `-lluxaccel``libluxaccel.{so,dylib}` |
```bash
# Real build (Linux/CUDA, Spark GB10, etc.)
CGO_LDFLAGS="-L/usr/local/lib -lluxaccel" \
LD_LIBRARY_PATH=/usr/local/lib \
go build -tags lux_accel_real ./...
# Real build (Apple Silicon / Metal)
CGO_LDFLAGS="-L/opt/homebrew/lib -lluxaccel" \
DYLD_LIBRARY_PATH=/opt/homebrew/lib \
go build -tags lux_accel_real ./...
```
The earlier scheme used `__attribute__((weak))` on every C stub to
allow the loader to override at runtime via `LD_PRELOAD`. Modern
linkers (with `--as-needed` default) resolve the references against
the in-Go-object weak stubs and drop the `DT_NEEDED` for libluxaccel.
Result: GPU paths silently ran on CPU. An audit on a Spark GB10
host (2026-06-05) caught this — full ML-DSA bench ran at SM% 9-10%
and 16 W. The build-tag flip is the fix: stub bodies and link
directive can never both be in one build.
## Related
- `lux/gpu` - Low-level GPU array operations
+239
View File
@@ -455,6 +455,245 @@ func DilithiumVerify(msg, sig, pk []byte) (bool, error) {
return session.Lattice().DilithiumVerify(msgTensor.Untyped(), sigTensor.Untyped(), pkTensor.Untyped())
}
// =============================================================================
// ML-DSA (FIPS 204) batched operations
// =============================================================================
// mldsaSizes returns (pk_bytes, sk_bytes, sig_bytes) for a FIPS 204 mode in
// {MLDSAMode44, MLDSAMode65, MLDSAMode87}. Returns (0, 0, 0) for unknown modes.
func mldsaSizes(mode int) (pk, sk, sig int) {
switch mode {
case MLDSAMode44:
return MLDSA44PublicKeySize, MLDSA44SecretKeySize, MLDSA44SignatureSize
case MLDSAMode65:
return MLDSA65PublicKeySize, MLDSA65SecretKeySize, MLDSA65SignatureSize
case MLDSAMode87:
return MLDSA87PublicKeySize, MLDSA87SecretKeySize, MLDSA87SignatureSize
default:
return 0, 0, 0
}
}
// MLDSAVerifyBatch verifies n ML-DSA signatures at the given FIPS 204 mode
// in {MLDSAMode44, MLDSAMode65, MLDSAMode87}. msgs, sigs, pks are flattened
// byte slices in row-major order with the per-mode widths. results[i] is set
// to true iff sigs[i] verifies under pks[i] for msgs[i]. The msgWidth
// argument pins the per-message tensor stride (callers MUST pad to a fixed
// width — the FIPS 204 verifier hashes the full padded width into mu).
//
// Returns ErrNotSupported when the GPU substrate or libluxaccel impl has
// not yet shipped the operation — callers should fall through to the
// per-element CPU verify path. Returns ErrBatchSizeMismatch on shape
// mismatch, ErrInvalidArgument on unknown mode.
func MLDSAVerifyBatch(mode int, msgs, sigs, pks [][]byte, msgWidth int) ([]bool, error) {
n := len(msgs)
if n == 0 || n != len(sigs) || n != len(pks) {
return nil, ErrBatchSizeMismatch
}
pkSize, _, sigSize := mldsaSizes(mode)
if pkSize == 0 {
return nil, ErrInvalidArgument
}
if !Available() || n < MLDSABatchThreshold {
return nil, ErrNotSupported
}
session, err := DefaultSession()
if err != nil {
return nil, err
}
msgFlat, err := flattenFixed(msgs, msgWidth)
if err != nil {
return nil, err
}
sigFlat, err := flattenFixed(sigs, sigSize)
if err != nil {
return nil, err
}
pkFlat, err := flattenFixed(pks, pkSize)
if err != nil {
return nil, err
}
msgTensor, err := NewTensorWithData[uint8](session, []int{n, msgWidth}, msgFlat)
if err != nil {
return nil, err
}
defer msgTensor.Close()
sigTensor, err := NewTensorWithData[uint8](session, []int{n, sigSize}, sigFlat)
if err != nil {
return nil, err
}
defer sigTensor.Close()
pkTensor, err := NewTensorWithData[uint8](session, []int{n, pkSize}, pkFlat)
if err != nil {
return nil, err
}
defer pkTensor.Close()
resTensor, err := NewTensor[uint8](session, []int{n})
if err != nil {
return nil, err
}
defer resTensor.Close()
if err := session.Lattice().MLDSAVerifyBatch(mode, msgTensor.Untyped(), sigTensor.Untyped(), pkTensor.Untyped(), resTensor.Untyped()); err != nil {
return nil, err
}
raw, err := resTensor.ToSlice()
if err != nil {
return nil, err
}
out := make([]bool, n)
for i, b := range raw {
out[i] = b != 0
}
return out, nil
}
// MLDSASignBatch signs n messages with ML-DSA at the given FIPS 204 mode
// in {MLDSAMode44, MLDSAMode65, MLDSAMode87}. msgs and sks are flattened
// byte slices in row-major order; the returned [][]byte is one sig per
// input.
//
// Returns ErrNotSupported when the substrate has not shipped the op.
func MLDSASignBatch(mode int, msgs, sks [][]byte, msgWidth int) ([][]byte, error) {
n := len(msgs)
if n == 0 || n != len(sks) {
return nil, ErrBatchSizeMismatch
}
_, skSize, sigSize := mldsaSizes(mode)
if skSize == 0 {
return nil, ErrInvalidArgument
}
if !Available() || n < MLDSABatchThreshold {
return nil, ErrNotSupported
}
session, err := DefaultSession()
if err != nil {
return nil, err
}
msgFlat, err := flattenFixed(msgs, msgWidth)
if err != nil {
return nil, err
}
skFlat, err := flattenFixed(sks, skSize)
if err != nil {
return nil, err
}
msgTensor, err := NewTensorWithData[uint8](session, []int{n, msgWidth}, msgFlat)
if err != nil {
return nil, err
}
defer msgTensor.Close()
skTensor, err := NewTensorWithData[uint8](session, []int{n, skSize}, skFlat)
if err != nil {
return nil, err
}
defer skTensor.Close()
sigTensor, err := NewTensor[uint8](session, []int{n, sigSize})
if err != nil {
return nil, err
}
defer sigTensor.Close()
if err := session.Lattice().MLDSASignBatch(mode, msgTensor.Untyped(), skTensor.Untyped(), sigTensor.Untyped()); err != nil {
return nil, err
}
flat, err := sigTensor.ToSlice()
if err != nil {
return nil, err
}
out := make([][]byte, n)
for i := 0; i < n; i++ {
sig := make([]byte, sigSize)
copy(sig, flat[i*sigSize:(i+1)*sigSize])
out[i] = sig
}
return out, nil
}
// LatticeNTTMLDSABatch performs the in-place forward (inverse=false) or
// inverse (inverse=true) Number-Theoretic Transform over Z_q[X]/(X^256 + 1)
// with q = 8380417 (the FIPS 204 ML-DSA prime), batched across n=len(polys)
// polynomials. Each polys[i] MUST have length MLDSANTTPolyLen (256). The
// transform writes back into the input slice.
//
// Byte-equal to PQCLEAN_MLDSA65_CLEAN_ntt (forward). The dispatcher
// engages the GPU substrate only when len(polys) >= MLDSABatchThreshold
// (default 8); below threshold ErrNotSupported is returned so callers
// route to the per-poly CPU oracle.
func LatticeNTTMLDSABatch(polys [][]int32, inverse bool) error {
n := len(polys)
if n == 0 {
return ErrBatchSizeMismatch
}
for i, p := range polys {
if len(p) != MLDSANTTPolyLen {
_ = i
return ErrShapeMismatch
}
}
if !Available() || n < MLDSABatchThreshold {
return ErrNotSupported
}
session, err := DefaultSession()
if err != nil {
return err
}
// Flatten into a single [n*256]int32 backing buffer; cgo will see
// the contiguous bytes once it lands on the C side. We read back
// via ToSlice() into the same flat buffer, then deinterleave.
flat := make([]int32, n*MLDSANTTPolyLen)
for i, p := range polys {
copy(flat[i*MLDSANTTPolyLen:(i+1)*MLDSANTTPolyLen], p)
}
tensor, err := NewTensorWithData[int32](session, []int{n, MLDSANTTPolyLen}, flat)
if err != nil {
return err
}
defer tensor.Close()
if err := session.Lattice().LatticeNTTMLDSABatch(tensor.Untyped(), inverse); err != nil {
return err
}
out, err := tensor.ToSlice()
if err != nil {
return err
}
for i := 0; i < n; i++ {
copy(polys[i], out[i*MLDSANTTPolyLen:(i+1)*MLDSANTTPolyLen])
}
return nil
}
// flattenFixed concatenates a [][]byte where every row MUST be exactly
// `width` bytes. Returns ErrShapeMismatch on any width violation.
func flattenFixed(rows [][]byte, width int) ([]byte, error) {
out := make([]byte, len(rows)*width)
for i, r := range rows {
if len(r) != width {
return nil, ErrShapeMismatch
}
copy(out[i*width:(i+1)*width], r)
}
return out, nil
}
// =============================================================================
// ZK Operations (NTT, MSM)
// =============================================================================
+1 -1
View File
@@ -8,7 +8,7 @@
// package; it CGO-routes to the lux_crypto_<alg> static libraries.
//
// Phase 1: secp256k1 (ecrecover, address derivation). Future phases add
// bls12381, mldsa, mlkem, slhdsa, corona, frost, cggmp21, sha256,
// bls12381, mldsa, mlkem, slhdsa, ringtail, frost, cggmp21, sha256,
// blake2b, blake3, kzg, hpke, ipa, lamport, ecies, etc.
//
// Build matrix:
+2 -2
View File
@@ -65,8 +65,8 @@ func CryptoSecp256k1Ecrecover(hash, r, s []byte, v byte) ([]byte, error) {
// (hash || r || s || v_byte) tuples, 97 bytes each.
//
// Returns:
// * pubkeys: n*64 bytes, recovered uncompressed public keys
// * statuses: n bytes, 0 on success, nonzero error code per tuple
// - pubkeys: n*64 bytes, recovered uncompressed public keys
// - statuses: n bytes, 0 on success, nonzero error code per tuple
func CryptoSecp256k1EcrecoverBatch(inputs []byte) (pubkeys, statuses []byte, err error) {
if len(inputs) == 0 {
return nil, nil, nil
+27
View File
@@ -85,3 +85,30 @@ func NTTInverse(_, _ []uint64, _ uint64) error {
func MSM(_, _ [][]byte) ([]byte, error) {
return nil, ErrNotSupported
}
// MLDSAVerifyBatch returns ErrNotSupported in non-CGO builds.
func MLDSAVerifyBatch(_ int, _, _, _ [][]byte, _ int) ([]bool, error) {
return nil, ErrNotSupported
}
// MLDSASignBatch returns ErrNotSupported in non-CGO builds.
func MLDSASignBatch(_ int, _, _ [][]byte, _ int) ([][]byte, error) {
return nil, ErrNotSupported
}
// LatticeNTTMLDSABatch returns ErrNotSupported in non-CGO builds.
func LatticeNTTMLDSABatch(polys [][]int32, _ bool) error {
// Validate the batch shape before the (absent) GPU dispatch decision,
// matching the cgo path so callers get the same error regardless of
// build: a wrong-length poly is ErrShapeMismatch, empty is
// ErrBatchSizeMismatch; otherwise no backend → ErrNotSupported.
if len(polys) == 0 {
return ErrBatchSizeMismatch
}
for _, p := range polys {
if len(p) != MLDSANTTPolyLen {
return ErrShapeMismatch
}
}
return ErrNotSupported
}
+33 -1
View File
@@ -21,13 +21,45 @@ const (
KyberSharedKeySize = 32
)
// Dilithium sizes (ML-DSA-65)
// Dilithium sizes (ML-DSA-65). The DilithiumSecretKeySize=4016 constant
// predates the FIPS 204 final fix that pinned the ML-DSA-65 secret key
// at 4032 bytes; new code should prefer the MLDSA* sizes below.
const (
DilithiumPublicKeySize = 1952
DilithiumSecretKeySize = 4016
DilithiumSignatureSize = 3309
)
// ML-DSA / FIPS 204 sizes per NIST level. Mode encoding matches the
// luxcpp/crypto/mldsa C ABI: 2 = ML-DSA-44, 3 = ML-DSA-65, 5 = ML-DSA-87.
const (
// Mode IDs.
MLDSAMode44 = 2
MLDSAMode65 = 3
MLDSAMode87 = 5
// Per-mode tensor widths (FIPS 204).
MLDSA44PublicKeySize = 1312
MLDSA44SecretKeySize = 2560
MLDSA44SignatureSize = 2420
MLDSA65PublicKeySize = 1952
MLDSA65SecretKeySize = 4032
MLDSA65SignatureSize = 3309
MLDSA87PublicKeySize = 2592
MLDSA87SecretKeySize = 4896
MLDSA87SignatureSize = 4627
// ML-DSA NTT poly width (FIPS 204 fixed at N = 256).
MLDSANTTPolyLen = 256
// MLDSABatchThreshold: minimum batch size at which the GPU
// dispatch path is engaged. Below this, callers should fall
// through to the per-element CPU oracle to amortise launch cost.
MLDSABatchThreshold = 8
)
var (
// ErrNoBackends indicates no GPU backends are available.
ErrNoBackends = errors.New("accel: no GPU backends available")
+47 -24
View File
@@ -14,15 +14,21 @@
package capi
/*
// System and homebrew paths for headers
#cgo CFLAGS: -I/usr/local/include -I/opt/homebrew/include -I${SRCDIR}/../../include
// Fallback to vendored header shipped with this module — works in any
// fresh-clone / CI environment without luxcpp pre-installed. The vendored
// header declares the C ABI; the stub.go file provides weak symbols so
// builds succeed even when libluxaccel is not linked.
// Header search order — VENDORED FIRST.
//
// The vendored c_api.h shipped under ${SRCDIR}/include is the single
// source of truth for the C ABI. Putting it first guarantees that every
// declaration (including ones added since the last luxcpp install) is
// visible to cgo regardless of what version of the public luxcpp header
// is sitting in /usr/local/include or /opt/homebrew/include. The stub.go
// file provides matching weak symbols so builds succeed even when
// libluxaccel is not linked.
#cgo CFLAGS: -I${SRCDIR}/include
// Optional installed headers (older snapshots are tolerated — they
// supply nothing the vendored header doesn't already declare).
#cgo CFLAGS: -I/usr/local/include -I/opt/homebrew/include -I${SRCDIR}/../../include
// Fallback to local luxcpp install (relative to this file)
#cgo CFLAGS: -I${SRCDIR}/../../../../luxcpp/install/include
@@ -505,29 +511,46 @@ func SLHDSAVerifyBatch(session *Session, mode int, msgs, sigs, pks, results *Ten
// The results tensor is populated with one uint8 per signature (1 = valid,
// 0 = invalid).
//
// Until the lux-accel C-API exposes lux_mldsa_verify_batch (in development),
// this returns ErrNotSupported so the Go-side dispatcher falls back to its
// per-element verify path. The function is wired here so consumers can call
// it before the substrate ships without breaking the build.
// The C ABI symbol `lux_mldsa_verify_batch` is declared in c_api.h and
// resolved at link time. When libluxaccel ships an implementation it
// runs FIPS 204 batched verify on the active backend; when no native
// library is linked the weak-symbol stub in stub.go returns
// LUX_NO_BACKEND, which surfaces here as ErrNoBackends. Either way,
// callers see the Go-level error and can fall through to the per-
// element CPU verify path — the substrate boundary is the wire, not a
// stub.
func MLDSAVerifyBatch(session *Session, mode int, msgs, sigs, pks, results *Tensor) error {
_ = session
_ = mode
_ = msgs
_ = sigs
_ = pks
_ = results
return ErrNotSupported
status := C.lux_mldsa_verify_batch(session.handle, C.int(mode), msgs.handle, sigs.handle, pks.handle, results.handle)
return statusToError(status)
}
// MLDSASignBatch batch-signs ML-DSA / Dilithium (FIPS 204) messages.
// mode as for MLDSAVerifyBatch. See doc on MLDSAVerifyBatch for ABI status.
func MLDSASignBatch(session *Session, mode int, msgs, sks, sigs *Tensor) error {
_ = session
_ = mode
_ = msgs
_ = sks
_ = sigs
return ErrNotSupported
status := C.lux_mldsa_sign_batch(session.handle, C.int(mode), msgs.handle, sks.handle, sigs.handle)
return statusToError(status)
}
// LatticeNTTMLDSABatch performs the in-place forward (inverse=false) or
// inverse (inverse=true) Number-Theoretic Transform over Z_q[X]/(X^256 + 1)
// with q = 8380417 (the ML-DSA prime), batched across n polynomials.
//
// The polys tensor MUST be LUX_DTYPE_I32 with shape [n, 256]; the transform
// is in-place. Byte-equal to PQCLEAN_MLDSA65_CLEAN_ntt for the forward
// direction; the inverse direction (when implemented by the backend) is
// the matching PQCLEAN_MLDSA65_CLEAN_invntt_tomont.
//
// When libluxaccel has not linked an implementation, the weak stub
// returns LUX_NO_BACKEND which surfaces as ErrNoBackends — callers
// then route to their per-poly CPU NTT (Pulsar does this via the
// luxfi/lattice/v7 dispatcher).
func LatticeNTTMLDSABatch(session *Session, polys *Tensor, inverse bool) error {
var inv C.int
if inverse {
inv = 1
}
status := C.lux_lattice_ntt_mldsa_batch(session.handle, polys.handle, inv)
return statusToError(status)
}
// FHE operations
-28
View File
@@ -1,28 +0,0 @@
//go:build cgo && accel_native && darwin
// Package capi — opt-in native-library link directives.
//
// Default builds resolve every C symbol against the weak stubs in
// stub.go, which keeps the binary buildable on hosts where libluxaccel
// is not installed. Building with `-tags=accel_native` switches the
// link to the real libluxaccel, at which point the weak stubs are
// overridden and accel.Available() reflects the real backend count
// (Metal on Apple Silicon, CUDA on Linux/NVIDIA).
//
// Required host install:
// /opt/homebrew/lib/libluxaccel.dylib (or /usr/local/lib on Intel)
// Plus a Metal plugin at one of:
// /opt/homebrew/lib/lux/plugins/lux_metal.plugin
// ~/.lux/plugins/lux_metal.plugin
// $LUX_PLUGIN_PATH/lux_metal.plugin
//
// libluxaccel is self-contained: it does not require linking against
// libluxgpu or libluxcrypto at the Go consumer site. The luxcpp build
// links those statically into libluxaccel before install.
package capi
/*
#cgo darwin LDFLAGS: -lluxaccel
*/
import "C"
-18
View File
@@ -1,18 +0,0 @@
//go:build cgo && accel_native && linux
// Package capi — opt-in native-library link directives (Linux).
// See capi_native_darwin.go for the policy. Required host install:
// /usr/local/lib/libluxaccel.so
// Plus a CUDA plugin under the configured LUX_PLUGIN_PATH.
//
// libluxaccel.so is self-contained: it does not require linking
// against libluxgpu or libluxcrypto at the Go consumer site. The
// luxcpp build links those statically into libluxaccel.so before
// install.
package capi
/*
#cgo linux LDFLAGS: -lluxaccel
*/
import "C"
+73
View File
@@ -241,6 +241,79 @@ LUX_API lux_status lux_dilithium_sign(lux_session session, lux_tensor msg,
LUX_API lux_status lux_dilithium_verify(lux_session session, lux_tensor msg,
lux_tensor sig, lux_tensor pk, int* valid);
// =============================================================================
// ML-DSA / Dilithium (FIPS 204) operations
// =============================================================================
//
// ML-DSA is a module-lattice signature scheme (FIPS 204, formerly Dilithium).
// The mode encoding follows the luxcpp/crypto/mldsa C ABI:
// 2 -> ML-DSA-44 (NIST L2)
// 3 -> ML-DSA-65 (NIST L3) <-- canonical for Pulsar threshold profile
// 5 -> ML-DSA-87 (NIST L5)
//
// Per FIPS 204, key / signature widths per mode:
// ML-DSA-44 : pk=1312 sk=2560 sig=2420
// ML-DSA-65 : pk=1952 sk=4032 sig=3309
// ML-DSA-87 : pk=2592 sk=4896 sig=4627
//
// Tensor shapes (n = batch size):
// msgs : LUX_DTYPE_U8, shape [n, msg_width]
// sigs : LUX_DTYPE_U8, shape [n, sig_bytes]
// sks : LUX_DTYPE_U8, shape [n, sk_bytes]
// pks : LUX_DTYPE_U8, shape [n, pk_bytes]
// results : LUX_DTYPE_U8, shape [n] (1 = valid, 0 = invalid)
//
// Verify is deterministic per FIPS 204: GPU and CPU paths produce byte-
// identical accept/reject decisions per element. The results vector is dense
// (no early abort) so callers can audit per-signer failures.
//
// Sign in hedged mode is deterministic when the deterministic flag is set
// at the caller boundary; the per-mode implementation MUST select the same
// hedging mode as the caller-side CPU reference (PQClean ML-DSA-{44,65,87})
// to remain byte-equal for KAT.
//
// Until the libluxaccel implementation links these symbols against the
// PQClean reference (or the per-backend GPU plugin registers a strong
// override), the weak-symbol stubs in `internal/capi/stub.go` return
// LUX_NO_BACKEND. The Go-side dispatcher (LatticeOps.MLDSAVerifyBatch /
// MLDSASignBatch) then falls back to its per-element CPU verify path,
// which is byte-equivalent by construction (same FIPS 204 spec, same
// NIST KATs pass on both sides).
LUX_API lux_status lux_mldsa_sign_batch(lux_session session, int mode,
lux_tensor msgs, lux_tensor sks,
lux_tensor sigs);
LUX_API lux_status lux_mldsa_verify_batch(lux_session session, int mode,
lux_tensor msgs, lux_tensor sigs,
lux_tensor pks, lux_tensor results);
// =============================================================================
// Lattice NTT batched primitives (FIPS 204 ML-DSA-65 forward / inverse)
// =============================================================================
//
// `lux_lattice_ntt_mldsa_batch` performs the in-place forward Number-Theoretic
// Transform over Z_q[X]/(X^256 + 1) with q = 8380417 (the ML-DSA prime),
// batched across `n` polynomials. Byte-equal to PQCLEAN_MLDSA65_CLEAN_ntt
// (Cooley-Tukey butterflies in Montgomery domain, 8 levels, 256-entry zeta
// table shared across all backends — see lux-private/gpu-kernels/ops/lattice/
// ntt_mldsa/op.yaml).
//
// Pulsar's Round-2 dispatches 22 forward NTTs per party-attempt, which fits
// the `batch ≥ 8` GPU-dispatch threshold cleanly. Below that threshold the
// Go-side dispatcher MUST route to its per-poly CPU path to amortise the
// kernel-launch cost (see `ops/lattice/lattice.go` for the threshold logic).
//
// Tensor shape:
// polys : LUX_DTYPE_I32, shape [n, 256] — in-place forward NTT
//
// `inverse` selects forward (0) vs inverse (non-zero) NTT. The forward path
// is the load-bearing one for Pulsar; the inverse path is exposed for
// symmetric polymul use-cases.
LUX_API lux_status lux_lattice_ntt_mldsa_batch(lux_session session,
lux_tensor polys,
int inverse);
// =============================================================================
// SLH-DSA / Magnetar (FIPS 205) operations
// =============================================================================
+43
View File
@@ -0,0 +1,43 @@
//go:build cgo && lux_accel_real
// Package capi real-build link directives.
//
// One and only one of `stub_default.go` or `real.go` is in any given
// build. The selector is the `lux_accel_real` build tag — see the
// header comment on stub_default.go for the design rationale.
//
// This file holds exactly two things:
//
// 1. `#cgo LDFLAGS: -lluxaccel` — link the binary against the real
// native library. Combined with the `-L` search paths and rpaths
// already declared in capi.go, the dynamic loader finds
// libluxaccel.{so,dylib} at one of:
//
// /usr/local/lib (canonical Linux install)
// /opt/homebrew/lib (Apple Silicon Homebrew)
// ${SRCDIR}/../../../../luxcpp/install/lib (local checkout)
//
// 2. `#include <lux/accel/c_api.h>` — declares every `lux_*` entry
// point as a normal (non-weak) extern. capi.go calls these from
// Go via cgo wrappers; under this build tag the wrappers resolve
// directly to libluxaccel's strong definitions, with no stub
// bodies anywhere in the binary.
//
// Required host install (verified at boot via accel.Available()):
//
// Linux : /usr/local/lib/libluxaccel.so.0.1.2 (plus CUDA plugin
// under $LUX_PLUGIN_PATH or /usr/local/lib/lux/plugins/)
// Darwin : /opt/homebrew/lib/libluxaccel.dylib (plus Metal plugin
// under /opt/homebrew/lib/lux/plugins/ or ~/.lux/plugins/)
//
// libluxaccel is self-contained: it links libluxgpu + libluxcrypto
// statically at build time inside luxcpp. Go consumers only need
// `-lluxaccel`.
package capi
/*
#cgo LDFLAGS: -lluxaccel
#include <lux/accel/c_api.h>
*/
import "C"
-100
View File
@@ -1,100 +0,0 @@
//go:build cgo && !accel_native
// Package capi stub implementations.
// When the real libluxaccel is not installed, these weak-symbol C functions
// provide default implementations that return LUX_NO_BACKEND so the build
// succeeds without the native library.
//
// The accel_native build tag REMOVES this file from the build. That makes
// every C symbol referenced by capi.go unresolved, which forces the linker
// to pull in libluxaccel from -lluxaccel (the directive in
// capi_native_{linux,darwin}.go). Without this gating, modern linkers
// (with --as-needed default) see the weak stubs resolve the symbols and
// drop libluxaccel from the binary's DT_NEEDED — at which point
// accel.Init() returns "no backends" because the real plugin-discovery
// code never runs.
package capi
/*
#include <lux/accel/c_api.h>
#include <stdlib.h>
#include <string.h>
// Weak-symbol stubs: overridden by real libluxaccel when linked.
__attribute__((weak)) const char* lux_version(void) { return "stub-0.0.0"; }
__attribute__((weak)) const char* lux_get_error(void) { return "no accel library"; }
__attribute__((weak)) lux_status lux_init(void) { return LUX_NO_BACKEND; }
__attribute__((weak)) void lux_shutdown(void) {}
__attribute__((weak)) lux_status lux_load_backend(const char* p) { return LUX_NO_BACKEND; }
__attribute__((weak)) int lux_backend_count(void) { return 0; }
__attribute__((weak)) lux_backend_type lux_backend_type_at(int i) { return 0; }
__attribute__((weak)) int lux_device_count(lux_backend_type b) { return 0; }
__attribute__((weak)) lux_status lux_get_device_info(lux_backend_type b, int i, lux_device_info* o) {
if (o) memset(o, 0, sizeof(*o));
return LUX_NO_BACKEND;
}
__attribute__((weak)) lux_status lux_session_create(lux_session* s) { if(s)*s=NULL; return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_session_create_with_backend(lux_backend_type b, lux_session* s) { if(s)*s=NULL; return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_session_create_with_device(lux_backend_type b, int d, lux_session* s) { if(s)*s=NULL; return LUX_NO_BACKEND; }
__attribute__((weak)) void lux_session_destroy(lux_session s) {}
__attribute__((weak)) lux_status lux_session_sync(lux_session s) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_tensor_create(lux_session s, lux_dtype d, const size_t* sh, size_t nd, lux_tensor* t) { if(t)*t=NULL; return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_tensor_create_with_data(lux_session s, lux_dtype d, const size_t* sh, size_t nd, const void* data, size_t len, lux_tensor* t) { if(t)*t=NULL; return LUX_NO_BACKEND; }
__attribute__((weak)) void lux_tensor_destroy(lux_tensor t) {}
__attribute__((weak)) size_t lux_tensor_ndim(lux_tensor t) { return 0; }
__attribute__((weak)) size_t lux_tensor_shape(lux_tensor t, size_t d) { return 0; }
__attribute__((weak)) size_t lux_tensor_numel(lux_tensor t) { return 0; }
__attribute__((weak)) size_t lux_tensor_bytes(lux_tensor t) { return 0; }
__attribute__((weak)) lux_dtype lux_tensor_dtype(lux_tensor t) { return 0; }
__attribute__((weak)) lux_status lux_tensor_to_host(lux_tensor t, void* d, size_t n) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_tensor_from_host(lux_tensor t, const void* d, size_t n) { return LUX_NO_BACKEND; }
// ML ops
__attribute__((weak)) lux_status lux_matmul(lux_session s, lux_tensor a, lux_tensor b, lux_tensor c) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_relu(lux_session s, lux_tensor i, lux_tensor o) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_gelu(lux_session s, lux_tensor i, lux_tensor o) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_softmax(lux_session s, lux_tensor i, lux_tensor o, int ax) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_layer_norm(lux_session s, lux_tensor i, lux_tensor g, lux_tensor b, lux_tensor o, float e) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_attention(lux_session s, lux_tensor q, lux_tensor k, lux_tensor v, lux_tensor o, float sc) { return LUX_NO_BACKEND; }
// Crypto ops
__attribute__((weak)) lux_status lux_sha256(lux_session s, lux_tensor i, lux_tensor o) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_keccak256(lux_session s, lux_tensor i, lux_tensor o) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_poseidon(lux_session s, lux_tensor i, lux_tensor o) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_ecdsa_verify_batch(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_ed25519_verify_batch(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_bls_verify_batch(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_merkle_root(lux_session s, lux_tensor l, lux_tensor r) { return LUX_NO_BACKEND; }
// ZK ops
__attribute__((weak)) lux_status lux_ntt(lux_session s, lux_tensor i, lux_tensor o, lux_tensor rt, uint64_t m) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_intt(lux_session s, lux_tensor i, lux_tensor o, lux_tensor ir, uint64_t m) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_msm(lux_session s, lux_tensor sc, lux_tensor ba, lux_tensor r) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_poly_mul(lux_session s, lux_tensor a, lux_tensor b, lux_tensor c, uint64_t m) { return LUX_NO_BACKEND; }
// Lattice/PQC ops
__attribute__((weak)) lux_status lux_kyber_keygen(lux_session s, lux_tensor pk, lux_tensor sk) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_kyber_encaps(lux_session s, lux_tensor pk, lux_tensor ct, lux_tensor ss) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_kyber_decaps(lux_session s, lux_tensor ct, lux_tensor sk, lux_tensor ss) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_dilithium_sign(lux_session s, lux_tensor m, lux_tensor sk, lux_tensor sg) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_dilithium_verify(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, int* v) { if(v)*v=0; return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_slhdsa_sign_batch(lux_session s, int mode, lux_tensor m, lux_tensor sk, lux_tensor sg) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_slhdsa_verify_batch(lux_session s, int mode, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { return LUX_NO_BACKEND; }
// FHE ops
__attribute__((weak)) lux_status lux_bfv_encrypt(lux_session s, lux_tensor p, lux_tensor pk, lux_tensor c) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_bfv_decrypt(lux_session s, lux_tensor c, lux_tensor sk, lux_tensor p) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_bfv_add(lux_session s, lux_tensor a, lux_tensor b, lux_tensor r) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_bfv_multiply(lux_session s, lux_tensor a, lux_tensor b, lux_tensor rk, lux_tensor r) { return LUX_NO_BACKEND; }
// DEX ops
__attribute__((weak)) lux_status lux_constant_product_swap(lux_session s, lux_tensor rx, lux_tensor ry, lux_tensor ai, int d, lux_tensor ao, float f) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_compute_twap(lux_session s, lux_tensor p, lux_tensor ts, uint64_t st, uint64_t en, lux_tensor tw) { return LUX_NO_BACKEND; }
__attribute__((weak)) lux_status lux_match_orders(lux_session s, lux_tensor bi, lux_tensor ak, lux_tensor mt, lux_tensor pr, lux_tensor am) { return LUX_NO_BACKEND; }
*/
import "C"
+126
View File
@@ -0,0 +1,126 @@
//go:build cgo && !lux_accel_real
// Package capi default-build stub implementations.
//
// One and only one of `stub_default.go` or `real.go` is in any given
// build. The selector is the `lux_accel_real` build tag:
//
// go build ./... # this file is compiled,
// # every C entry returns
// # LUX_NO_BACKEND. Portable
// # CPU-only binary.
//
// go build -tags lux_accel_real ./... # real.go is compiled instead,
// # binary links -lluxaccel and
// # dispatches to the native
// # CUDA / Metal plugin.
//
// History: an earlier revision used `__attribute__((weak))` on every
// stub and tried to override at link time when libluxaccel was present.
// Modern linkers with --as-needed default resolved the references
// against the weak stubs (already in the Go object) and dropped the
// DT_NEEDED entry for libluxaccel entirely. Result: GPU paths silently
// ran on CPU. Audit on a Spark GB10 host (2026-06-05) measured 9-10%
// SM during a full ML-DSA bench that was supposed to be GPU-accelerated.
// The build-tag split removes the weak-symbol ambiguity: either the
// stub bodies compile, or the link to the real library does — never
// both.
//
// All stubs return LUX_NO_BACKEND. The Go-side ops dispatcher (see
// `crypto/backend.Resolve` in luxfi/crypto and `ops/lattice/lattice.go`
// in this module) maps LUX_NO_BACKEND to a per-element CPU fallback,
// so default-tag binaries remain functionally complete — just slower.
package capi
/*
#include <lux/accel/c_api.h>
#include <stdlib.h>
#include <string.h>
static const char* _stub_version(void) { return "stub-0.0.0"; }
static const char* _stub_error(void) { return "no accel library (built without -tags lux_accel_real)"; }
lux_status lux_init(void) { return LUX_NO_BACKEND; }
void lux_shutdown(void) {}
const char* lux_version(void) { return _stub_version(); }
const char* lux_get_error(void) { return _stub_error(); }
lux_status lux_load_backend(const char* p) { (void)p; return LUX_NO_BACKEND; }
int lux_backend_count(void) { return 0; }
lux_backend_type lux_backend_type_at(int i){ (void)i; return 0; }
int lux_device_count(lux_backend_type b) { (void)b; return 0; }
lux_status lux_get_device_info(lux_backend_type b, int i, lux_device_info* o) {
(void)b; (void)i;
if (o) memset(o, 0, sizeof(*o));
return LUX_NO_BACKEND;
}
lux_status lux_session_create(lux_session* s) { if(s)*s=NULL; return LUX_NO_BACKEND; }
lux_status lux_session_create_with_backend(lux_backend_type b, lux_session* s) { (void)b; if(s)*s=NULL; return LUX_NO_BACKEND; }
lux_status lux_session_create_with_device(lux_backend_type b, int d, lux_session* s) { (void)b; (void)d; if(s)*s=NULL; return LUX_NO_BACKEND; }
void lux_session_destroy(lux_session s) { (void)s; }
lux_status lux_session_sync(lux_session s) { (void)s; return LUX_NO_BACKEND; }
lux_status lux_session_get_device_info(lux_session s, lux_device_info* o) {
(void)s;
if (o) memset(o, 0, sizeof(*o));
return LUX_NO_BACKEND;
}
lux_status lux_tensor_create(lux_session s, lux_dtype d, const size_t* sh, size_t nd, lux_tensor* t) { (void)s; (void)d; (void)sh; (void)nd; if(t)*t=NULL; return LUX_NO_BACKEND; }
lux_status lux_tensor_create_with_data(lux_session s, lux_dtype d, const size_t* sh, size_t nd, const void* data, size_t len, lux_tensor* t) { (void)s; (void)d; (void)sh; (void)nd; (void)data; (void)len; if(t)*t=NULL; return LUX_NO_BACKEND; }
void lux_tensor_destroy(lux_tensor t) { (void)t; }
size_t lux_tensor_ndim(lux_tensor t) { (void)t; return 0; }
size_t lux_tensor_shape(lux_tensor t, size_t d) { (void)t; (void)d; return 0; }
size_t lux_tensor_numel(lux_tensor t) { (void)t; return 0; }
size_t lux_tensor_bytes(lux_tensor t) { (void)t; return 0; }
lux_dtype lux_tensor_dtype(lux_tensor t) { (void)t; return 0; }
lux_status lux_tensor_to_host(lux_tensor t, void* d, size_t n) { (void)t; (void)d; (void)n; return LUX_NO_BACKEND; }
lux_status lux_tensor_from_host(lux_tensor t, const void* d, size_t n) { (void)t; (void)d; (void)n; return LUX_NO_BACKEND; }
// ML ops
lux_status lux_matmul(lux_session s, lux_tensor a, lux_tensor b, lux_tensor c) { (void)s; (void)a; (void)b; (void)c; return LUX_NO_BACKEND; }
lux_status lux_relu(lux_session s, lux_tensor i, lux_tensor o) { (void)s; (void)i; (void)o; return LUX_NO_BACKEND; }
lux_status lux_gelu(lux_session s, lux_tensor i, lux_tensor o) { (void)s; (void)i; (void)o; return LUX_NO_BACKEND; }
lux_status lux_softmax(lux_session s, lux_tensor i, lux_tensor o, int ax) { (void)s; (void)i; (void)o; (void)ax; return LUX_NO_BACKEND; }
lux_status lux_layer_norm(lux_session s, lux_tensor i, lux_tensor g, lux_tensor b, lux_tensor o, float e) { (void)s; (void)i; (void)g; (void)b; (void)o; (void)e; return LUX_NO_BACKEND; }
lux_status lux_attention(lux_session s, lux_tensor q, lux_tensor k, lux_tensor v, lux_tensor o, float sc) { (void)s; (void)q; (void)k; (void)v; (void)o; (void)sc; return LUX_NO_BACKEND; }
// Crypto ops
lux_status lux_sha256(lux_session s, lux_tensor i, lux_tensor o) { (void)s; (void)i; (void)o; return LUX_NO_BACKEND; }
lux_status lux_keccak256(lux_session s, lux_tensor i, lux_tensor o) { (void)s; (void)i; (void)o; return LUX_NO_BACKEND; }
lux_status lux_poseidon(lux_session s, lux_tensor i, lux_tensor o) { (void)s; (void)i; (void)o; return LUX_NO_BACKEND; }
lux_status lux_ecdsa_verify_batch(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { (void)s; (void)m; (void)sg; (void)pk; (void)r; return LUX_NO_BACKEND; }
lux_status lux_ed25519_verify_batch(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { (void)s; (void)m; (void)sg; (void)pk; (void)r; return LUX_NO_BACKEND; }
lux_status lux_bls_verify_batch(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { (void)s; (void)m; (void)sg; (void)pk; (void)r; return LUX_NO_BACKEND; }
lux_status lux_merkle_root(lux_session s, lux_tensor l, lux_tensor r) { (void)s; (void)l; (void)r; return LUX_NO_BACKEND; }
// ZK ops
lux_status lux_ntt(lux_session s, lux_tensor i, lux_tensor o, lux_tensor rt, uint64_t m) { (void)s; (void)i; (void)o; (void)rt; (void)m; return LUX_NO_BACKEND; }
lux_status lux_intt(lux_session s, lux_tensor i, lux_tensor o, lux_tensor ir, uint64_t m) { (void)s; (void)i; (void)o; (void)ir; (void)m; return LUX_NO_BACKEND; }
lux_status lux_msm(lux_session s, lux_tensor sc, lux_tensor ba, lux_tensor r) { (void)s; (void)sc; (void)ba; (void)r; return LUX_NO_BACKEND; }
lux_status lux_poly_mul(lux_session s, lux_tensor a, lux_tensor b, lux_tensor c, uint64_t m) { (void)s; (void)a; (void)b; (void)c; (void)m; return LUX_NO_BACKEND; }
// Lattice / PQC ops
lux_status lux_kyber_keygen(lux_session s, lux_tensor pk, lux_tensor sk) { (void)s; (void)pk; (void)sk; return LUX_NO_BACKEND; }
lux_status lux_kyber_encaps(lux_session s, lux_tensor pk, lux_tensor ct, lux_tensor ss) { (void)s; (void)pk; (void)ct; (void)ss; return LUX_NO_BACKEND; }
lux_status lux_kyber_decaps(lux_session s, lux_tensor ct, lux_tensor sk, lux_tensor ss) { (void)s; (void)ct; (void)sk; (void)ss; return LUX_NO_BACKEND; }
lux_status lux_dilithium_sign(lux_session s, lux_tensor m, lux_tensor sk, lux_tensor sg) { (void)s; (void)m; (void)sk; (void)sg; return LUX_NO_BACKEND; }
lux_status lux_dilithium_verify(lux_session s, lux_tensor m, lux_tensor sg, lux_tensor pk, int* v) { (void)s; (void)m; (void)sg; (void)pk; if(v)*v=0; return LUX_NO_BACKEND; }
lux_status lux_slhdsa_sign_batch(lux_session s, int mode, lux_tensor m, lux_tensor sk, lux_tensor sg) { (void)s; (void)mode; (void)m; (void)sk; (void)sg; return LUX_NO_BACKEND; }
lux_status lux_slhdsa_verify_batch(lux_session s, int mode, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { (void)s; (void)mode; (void)m; (void)sg; (void)pk; (void)r; return LUX_NO_BACKEND; }
lux_status lux_mldsa_sign_batch(lux_session s, int mode, lux_tensor m, lux_tensor sk, lux_tensor sg) { (void)s; (void)mode; (void)m; (void)sk; (void)sg; return LUX_NO_BACKEND; }
lux_status lux_mldsa_verify_batch(lux_session s, int mode, lux_tensor m, lux_tensor sg, lux_tensor pk, lux_tensor r) { (void)s; (void)mode; (void)m; (void)sg; (void)pk; (void)r; return LUX_NO_BACKEND; }
lux_status lux_lattice_ntt_mldsa_batch(lux_session s, lux_tensor polys, int inv) { (void)s; (void)polys; (void)inv; return LUX_NO_BACKEND; }
// FHE ops
lux_status lux_bfv_encrypt(lux_session s, lux_tensor p, lux_tensor pk, lux_tensor c) { (void)s; (void)p; (void)pk; (void)c; return LUX_NO_BACKEND; }
lux_status lux_bfv_decrypt(lux_session s, lux_tensor c, lux_tensor sk, lux_tensor p) { (void)s; (void)c; (void)sk; (void)p; return LUX_NO_BACKEND; }
lux_status lux_bfv_add(lux_session s, lux_tensor a, lux_tensor b, lux_tensor r) { (void)s; (void)a; (void)b; (void)r; return LUX_NO_BACKEND; }
lux_status lux_bfv_multiply(lux_session s, lux_tensor a, lux_tensor b, lux_tensor rk, lux_tensor r) { (void)s; (void)a; (void)b; (void)rk; (void)r; return LUX_NO_BACKEND; }
// DEX ops
lux_status lux_constant_product_swap(lux_session s, lux_tensor rx, lux_tensor ry, lux_tensor ai, int d, lux_tensor ao, float f) { (void)s; (void)rx; (void)ry; (void)ai; (void)d; (void)ao; (void)f; return LUX_NO_BACKEND; }
lux_status lux_compute_twap(lux_session s, lux_tensor p, lux_tensor ts, uint64_t st, uint64_t en, lux_tensor tw) { (void)s; (void)p; (void)ts; (void)st; (void)en; (void)tw; return LUX_NO_BACKEND; }
lux_status lux_match_orders(lux_session s, lux_tensor bi, lux_tensor ak, lux_tensor mt, lux_tensor pr, lux_tensor am) { (void)s; (void)bi; (void)ak; (void)mt; (void)pr; (void)am; return LUX_NO_BACKEND; }
*/
import "C"
+3
View File
@@ -119,6 +119,9 @@ func (o *stubLatticeOps) PolynomialINTT(input, output *UntypedTensor, q uint32)
}
func (o *stubLatticeOps) PolynomialMul(a, b, c *UntypedTensor, q uint32) error { return ErrNoBackends }
func (o *stubLatticeOps) PolynomialAdd(a, b, c *UntypedTensor, q uint32) error { return ErrNoBackends }
func (o *stubLatticeOps) LatticeNTTMLDSABatch(polys *UntypedTensor, inverse bool) error {
return ErrNoBackends
}
type stubFHEOps struct{}
+70 -33
View File
@@ -20,6 +20,38 @@ import (
// CKKSAdd, CKKSMultiply, CKKSRescale, CKKSRotate, Bootstrap
// DEX: ConstantProductSwapBatch, MatchOrdersWithPriority, ComputeLiquidity,
// ComputePositionValue, CalculateFees, BatchSettlement
//
// Red CRITICAL Probe 9 — sentinel mismatch propagation contract:
// =================================================================
// The internal `capi` package and the public `accel` package each
// declare their own `ErrInvalidArgument` (etc.) sentinels — they are
// DISTINCT `errors.New(...)` values. A consumer that calls
//
// err := sess.Lattice().MLDSAVerifyBatch(...)
// if errors.Is(err, accel.ErrInvalidArgument) { ... hard error ... }
//
// will see `errors.Is(...) == false` if the cgo path returns the capi
// sentinel unwrapped — because errors.Is uses pointer-equality at the
// leaves of the unwrap chain and capi.ErrInvalidArgument is not
// accel.ErrInvalidArgument. This silently routes every "hard error"
// case to the recoverable-error branch (CPU fallback), defeating the
// entire M-1 propagation chain landed in:
//
// * lux/accel/ops/crypto/crypto_gpu.go:159 (SigMLDSA65)
// * lux/crypto/slhdsa/gpu.go:276, 479 (SLH-DSA verify/sign)
// * lux/crypto/pq/mldsa/gpu/gpu_cgo.go (ML-DSA verify/sign)
// * lux/crypto/mldsa/gpu.go (batchVerifyGPU/batchSignGPU)
// * lux/precompile/mldsa/contract.go:401 (precompile fail-closed)
// * lux/precompile/ai/ai_mining.go (AI mining fail-closed)
//
// The fix is `translateCapiError` (session_c.go:22-41), already
// applied to the NewSession* paths but NOT to the per-op cgo methods
// in this file. Every method that returned `capi.*(...)` unwrapped
// is now wrapped through translateCapiError so the public accel
// surface emits the accel.* sentinel that consumers errors.Is against.
//
// One sentinel, one source of truth, one propagation path. Adding a
// new cgo-routed op? Wrap the return through translateCapiError.
// cgoMLOps implements MLOps using CGO.
type cgoMLOps struct {
@@ -27,7 +59,7 @@ type cgoMLOps struct {
}
func (o *cgoMLOps) MatMul(a, b, c *UntypedTensor) error {
return capi.MatMul(o.session, getCAPITensor(a), getCAPITensor(b), getCAPITensor(c))
return translateCapiError(capi.MatMul(o.session, getCAPITensor(a), getCAPITensor(b), getCAPITensor(c)))
}
func (o *cgoMLOps) MatMulTranspose(a, b, c *UntypedTensor, transposeA, transposeB bool) error {
@@ -35,23 +67,23 @@ func (o *cgoMLOps) MatMulTranspose(a, b, c *UntypedTensor, transposeA, transpose
}
func (o *cgoMLOps) ReLU(input, output *UntypedTensor) error {
return capi.ReLU(o.session, getCAPITensor(input), getCAPITensor(output))
return translateCapiError(capi.ReLU(o.session, getCAPITensor(input), getCAPITensor(output)))
}
func (o *cgoMLOps) GELU(input, output *UntypedTensor) error {
return capi.GELU(o.session, getCAPITensor(input), getCAPITensor(output))
return translateCapiError(capi.GELU(o.session, getCAPITensor(input), getCAPITensor(output)))
}
func (o *cgoMLOps) Softmax(input, output *UntypedTensor, axis int) error {
return capi.Softmax(o.session, getCAPITensor(input), getCAPITensor(output), axis)
return translateCapiError(capi.Softmax(o.session, getCAPITensor(input), getCAPITensor(output), axis))
}
func (o *cgoMLOps) LayerNorm(input, gamma, beta, output *UntypedTensor, eps float32) error {
return capi.LayerNorm(o.session, getCAPITensor(input), getCAPITensor(gamma), getCAPITensor(beta), getCAPITensor(output), eps)
return translateCapiError(capi.LayerNorm(o.session, getCAPITensor(input), getCAPITensor(gamma), getCAPITensor(beta), getCAPITensor(output), eps))
}
func (o *cgoMLOps) Attention(q, k, v, output *UntypedTensor, scale float32) error {
return capi.Attention(o.session, getCAPITensor(q), getCAPITensor(k), getCAPITensor(v), getCAPITensor(output), scale)
return translateCapiError(capi.Attention(o.session, getCAPITensor(q), getCAPITensor(k), getCAPITensor(v), getCAPITensor(output), scale))
}
func (o *cgoMLOps) Conv2D(input, kernel, output *UntypedTensor, stride, padding [2]int) error {
@@ -92,27 +124,27 @@ type cgoCryptoOps struct {
}
func (o *cgoCryptoOps) SHA256(input, output *UntypedTensor) error {
return capi.SHA256(o.session, getCAPITensor(input), getCAPITensor(output))
return translateCapiError(capi.SHA256(o.session, getCAPITensor(input), getCAPITensor(output)))
}
func (o *cgoCryptoOps) Keccak256(input, output *UntypedTensor) error {
return capi.Keccak256(o.session, getCAPITensor(input), getCAPITensor(output))
return translateCapiError(capi.Keccak256(o.session, getCAPITensor(input), getCAPITensor(output)))
}
func (o *cgoCryptoOps) Poseidon(input, output *UntypedTensor) error {
return capi.Poseidon(o.session, getCAPITensor(input), getCAPITensor(output))
return translateCapiError(capi.Poseidon(o.session, getCAPITensor(input), getCAPITensor(output)))
}
func (o *cgoCryptoOps) ECDSAVerifyBatch(messages, signatures, pubkeys, results *UntypedTensor) error {
return capi.ECDSAVerifyBatch(o.session, getCAPITensor(messages), getCAPITensor(signatures), getCAPITensor(pubkeys), getCAPITensor(results))
return translateCapiError(capi.ECDSAVerifyBatch(o.session, getCAPITensor(messages), getCAPITensor(signatures), getCAPITensor(pubkeys), getCAPITensor(results)))
}
func (o *cgoCryptoOps) Ed25519VerifyBatch(messages, signatures, pubkeys, results *UntypedTensor) error {
return capi.Ed25519VerifyBatch(o.session, getCAPITensor(messages), getCAPITensor(signatures), getCAPITensor(pubkeys), getCAPITensor(results))
return translateCapiError(capi.Ed25519VerifyBatch(o.session, getCAPITensor(messages), getCAPITensor(signatures), getCAPITensor(pubkeys), getCAPITensor(results)))
}
func (o *cgoCryptoOps) BLSVerifyBatch(messages, signatures, pubkeys, results *UntypedTensor) error {
return capi.BLSVerifyBatch(o.session, getCAPITensor(messages), getCAPITensor(signatures), getCAPITensor(pubkeys), getCAPITensor(results))
return translateCapiError(capi.BLSVerifyBatch(o.session, getCAPITensor(messages), getCAPITensor(signatures), getCAPITensor(pubkeys), getCAPITensor(results)))
}
func (o *cgoCryptoOps) BLSAggregate(signatures, aggregated *UntypedTensor) error {
@@ -120,7 +152,7 @@ func (o *cgoCryptoOps) BLSAggregate(signatures, aggregated *UntypedTensor) error
}
func (o *cgoCryptoOps) MerkleRoot(leaves, root *UntypedTensor) error {
return capi.MerkleRoot(o.session, getCAPITensor(leaves), getCAPITensor(root))
return translateCapiError(capi.MerkleRoot(o.session, getCAPITensor(leaves), getCAPITensor(root)))
}
func (o *cgoCryptoOps) MerkleBatch(leavesSet, roots *UntypedTensor) error {
@@ -137,15 +169,15 @@ type cgoZKOps struct {
}
func (o *cgoZKOps) NTT(input, output, roots *UntypedTensor, modulus uint64) error {
return capi.NTT(o.session, getCAPITensor(input), getCAPITensor(output), getCAPITensor(roots), modulus)
return translateCapiError(capi.NTT(o.session, getCAPITensor(input), getCAPITensor(output), getCAPITensor(roots), modulus))
}
func (o *cgoZKOps) INTT(input, output, invRoots *UntypedTensor, modulus uint64) error {
return capi.INTT(o.session, getCAPITensor(input), getCAPITensor(output), getCAPITensor(invRoots), modulus)
return translateCapiError(capi.INTT(o.session, getCAPITensor(input), getCAPITensor(output), getCAPITensor(invRoots), modulus))
}
func (o *cgoZKOps) MSM(scalars, bases, result *UntypedTensor) error {
return capi.MSM(o.session, getCAPITensor(scalars), getCAPITensor(bases), getCAPITensor(result))
return translateCapiError(capi.MSM(o.session, getCAPITensor(scalars), getCAPITensor(bases), getCAPITensor(result)))
}
func (o *cgoZKOps) MSMBatch(scalars, bases, results *UntypedTensor) error {
@@ -153,7 +185,7 @@ func (o *cgoZKOps) MSMBatch(scalars, bases, results *UntypedTensor) error {
}
func (o *cgoZKOps) PolyMul(a, b, c *UntypedTensor, modulus uint64) error {
return capi.PolyMul(o.session, getCAPITensor(a), getCAPITensor(b), getCAPITensor(c), modulus)
return translateCapiError(capi.PolyMul(o.session, getCAPITensor(a), getCAPITensor(b), getCAPITensor(c), modulus))
}
func (o *cgoZKOps) PolyEval(coeffs, points, results *UntypedTensor, modulus uint64) error {
@@ -190,7 +222,7 @@ type cgoLatticeOps struct {
}
func (o *cgoLatticeOps) KyberKeyGen(pk, sk *UntypedTensor) error {
return capi.KyberKeyGen(o.session, getCAPITensor(pk), getCAPITensor(sk))
return translateCapiError(capi.KyberKeyGen(o.session, getCAPITensor(pk), getCAPITensor(sk)))
}
func (o *cgoLatticeOps) KyberKeyGenBatch(pk, sk *UntypedTensor) error {
@@ -198,7 +230,7 @@ func (o *cgoLatticeOps) KyberKeyGenBatch(pk, sk *UntypedTensor) error {
}
func (o *cgoLatticeOps) KyberEncaps(pk, ct, ss *UntypedTensor) error {
return capi.KyberEncaps(o.session, getCAPITensor(pk), getCAPITensor(ct), getCAPITensor(ss))
return translateCapiError(capi.KyberEncaps(o.session, getCAPITensor(pk), getCAPITensor(ct), getCAPITensor(ss)))
}
func (o *cgoLatticeOps) KyberEncapsBatch(pk, ct, ss *UntypedTensor) error {
@@ -206,7 +238,7 @@ func (o *cgoLatticeOps) KyberEncapsBatch(pk, ct, ss *UntypedTensor) error {
}
func (o *cgoLatticeOps) KyberDecaps(ct, sk, ss *UntypedTensor) error {
return capi.KyberDecaps(o.session, getCAPITensor(ct), getCAPITensor(sk), getCAPITensor(ss))
return translateCapiError(capi.KyberDecaps(o.session, getCAPITensor(ct), getCAPITensor(sk), getCAPITensor(ss)))
}
func (o *cgoLatticeOps) KyberDecapsBatch(ct, sk, ss *UntypedTensor) error {
@@ -218,7 +250,7 @@ func (o *cgoLatticeOps) DilithiumKeyGen(pk, sk *UntypedTensor) error {
}
func (o *cgoLatticeOps) DilithiumSign(msg, sk, sig *UntypedTensor) error {
return capi.DilithiumSign(o.session, getCAPITensor(msg), getCAPITensor(sk), getCAPITensor(sig))
return translateCapiError(capi.DilithiumSign(o.session, getCAPITensor(msg), getCAPITensor(sk), getCAPITensor(sig)))
}
func (o *cgoLatticeOps) DilithiumSignBatch(msgs, sk, sigs *UntypedTensor) error {
@@ -226,7 +258,8 @@ func (o *cgoLatticeOps) DilithiumSignBatch(msgs, sk, sigs *UntypedTensor) error
}
func (o *cgoLatticeOps) DilithiumVerify(msg, sig, pk *UntypedTensor) (bool, error) {
return capi.DilithiumVerify(o.session, getCAPITensor(msg), getCAPITensor(sig), getCAPITensor(pk))
ok, err := capi.DilithiumVerify(o.session, getCAPITensor(msg), getCAPITensor(sig), getCAPITensor(pk))
return ok, translateCapiError(err)
}
func (o *cgoLatticeOps) DilithiumVerifyBatch(msgs, sigs, pks, results *UntypedTensor) error {
@@ -234,19 +267,19 @@ func (o *cgoLatticeOps) DilithiumVerifyBatch(msgs, sigs, pks, results *UntypedTe
}
func (o *cgoLatticeOps) MLDSAVerifyBatch(mode int, msgs, sigs, pks, results *UntypedTensor) error {
return capi.MLDSAVerifyBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sigs), getCAPITensor(pks), getCAPITensor(results))
return translateCapiError(capi.MLDSAVerifyBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sigs), getCAPITensor(pks), getCAPITensor(results)))
}
func (o *cgoLatticeOps) MLDSASignBatch(mode int, msgs, sks, sigs *UntypedTensor) error {
return capi.MLDSASignBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sks), getCAPITensor(sigs))
return translateCapiError(capi.MLDSASignBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sks), getCAPITensor(sigs)))
}
func (o *cgoLatticeOps) SLHDSASignBatch(mode int, msgs, sks, sigs *UntypedTensor) error {
return capi.SLHDSASignBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sks), getCAPITensor(sigs))
return translateCapiError(capi.SLHDSASignBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sks), getCAPITensor(sigs)))
}
func (o *cgoLatticeOps) SLHDSAVerifyBatch(mode int, msgs, sigs, pks, results *UntypedTensor) error {
return capi.SLHDSAVerifyBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sigs), getCAPITensor(pks), getCAPITensor(results))
return translateCapiError(capi.SLHDSAVerifyBatch(o.session, mode, getCAPITensor(msgs), getCAPITensor(sigs), getCAPITensor(pks), getCAPITensor(results)))
}
func (o *cgoLatticeOps) PolynomialNTT(input, output *UntypedTensor, q uint32) error {
@@ -265,13 +298,17 @@ func (o *cgoLatticeOps) PolynomialAdd(a, b, c *UntypedTensor, q uint32) error {
return ErrNotSupported
}
func (o *cgoLatticeOps) LatticeNTTMLDSABatch(polys *UntypedTensor, inverse bool) error {
return translateCapiError(capi.LatticeNTTMLDSABatch(o.session, getCAPITensor(polys), inverse))
}
// cgoFHEOps implements FHEOps using CGO.
type cgoFHEOps struct {
session *capi.Session
}
func (o *cgoFHEOps) BFVEncrypt(plaintext, pk, ciphertext *UntypedTensor) error {
return capi.BFVEncrypt(o.session, getCAPITensor(plaintext), getCAPITensor(pk), getCAPITensor(ciphertext))
return translateCapiError(capi.BFVEncrypt(o.session, getCAPITensor(plaintext), getCAPITensor(pk), getCAPITensor(ciphertext)))
}
func (o *cgoFHEOps) BFVEncryptBatch(plaintexts, pk, ciphertexts *UntypedTensor) error {
@@ -279,15 +316,15 @@ func (o *cgoFHEOps) BFVEncryptBatch(plaintexts, pk, ciphertexts *UntypedTensor)
}
func (o *cgoFHEOps) BFVDecrypt(ciphertext, sk, plaintext *UntypedTensor) error {
return capi.BFVDecrypt(o.session, getCAPITensor(ciphertext), getCAPITensor(sk), getCAPITensor(plaintext))
return translateCapiError(capi.BFVDecrypt(o.session, getCAPITensor(ciphertext), getCAPITensor(sk), getCAPITensor(plaintext)))
}
func (o *cgoFHEOps) BFVAdd(ct1, ct2, result *UntypedTensor) error {
return capi.BFVAdd(o.session, getCAPITensor(ct1), getCAPITensor(ct2), getCAPITensor(result))
return translateCapiError(capi.BFVAdd(o.session, getCAPITensor(ct1), getCAPITensor(ct2), getCAPITensor(result)))
}
func (o *cgoFHEOps) BFVMultiply(ct1, ct2, relinKey, result *UntypedTensor) error {
return capi.BFVMultiply(o.session, getCAPITensor(ct1), getCAPITensor(ct2), getCAPITensor(relinKey), getCAPITensor(result))
return translateCapiError(capi.BFVMultiply(o.session, getCAPITensor(ct1), getCAPITensor(ct2), getCAPITensor(relinKey), getCAPITensor(result)))
}
func (o *cgoFHEOps) BFVMultiplyPlain(ct, plain, result *UntypedTensor) error {
@@ -332,7 +369,7 @@ type cgoDEXOps struct {
}
func (o *cgoDEXOps) ConstantProductSwap(reserveX, reserveY, amountIn *UntypedTensor, xToY bool, amountOut *UntypedTensor, fee float32) error {
return capi.ConstantProductSwap(o.session, getCAPITensor(reserveX), getCAPITensor(reserveY), getCAPITensor(amountIn), getCAPITensor(amountOut), xToY, fee)
return translateCapiError(capi.ConstantProductSwap(o.session, getCAPITensor(reserveX), getCAPITensor(reserveY), getCAPITensor(amountIn), getCAPITensor(amountOut), xToY, fee))
}
func (o *cgoDEXOps) ConstantProductSwapBatch(reserves, swaps, amounts *UntypedTensor, fee float32) error {
@@ -340,11 +377,11 @@ func (o *cgoDEXOps) ConstantProductSwapBatch(reserves, swaps, amounts *UntypedTe
}
func (o *cgoDEXOps) ComputeTWAP(prices, timestamps *UntypedTensor, start, end uint64, twap *UntypedTensor) error {
return capi.ComputeTWAP(o.session, getCAPITensor(prices), getCAPITensor(timestamps), getCAPITensor(twap), start, end)
return translateCapiError(capi.ComputeTWAP(o.session, getCAPITensor(prices), getCAPITensor(timestamps), getCAPITensor(twap), start, end))
}
func (o *cgoDEXOps) MatchOrders(bids, asks, matches, prices, amounts *UntypedTensor) error {
return capi.MatchOrders(o.session, getCAPITensor(bids), getCAPITensor(asks), getCAPITensor(matches), getCAPITensor(prices), getCAPITensor(amounts))
return translateCapiError(capi.MatchOrders(o.session, getCAPITensor(bids), getCAPITensor(asks), getCAPITensor(matches), getCAPITensor(prices), getCAPITensor(amounts)))
}
func (o *cgoDEXOps) MatchOrdersWithPriority(bids, asks, matches *UntypedTensor) error {
+14
View File
@@ -135,4 +135,18 @@ type LatticeOps interface {
// PolynomialAdd adds polynomials.
PolynomialAdd(a, b, c *UntypedTensor, q uint32) error
// LatticeNTTMLDSABatch performs the in-place forward (inverse=false) or
// inverse (inverse=true) NTT over Z_q[X]/(X^256 + 1) with q = 8380417 (the
// ML-DSA / FIPS 204 prime), batched across N polynomials.
//
// Tensor shape:
// polys : LUX_DTYPE_I32, shape [N, 256] — in-place transform
//
// Byte-equal to PQCLEAN_MLDSA65_CLEAN_ntt (forward) and
// PQCLEAN_MLDSA65_CLEAN_invntt_tomont (inverse). Pulsar Round-2 fits the
// batch=22 dispatch shape; below the GPU-dispatch threshold the caller
// MUST route to the per-poly CPU oracle (the threshold gating lives in
// the consumer, not in this primitive).
LatticeNTTMLDSABatch(polys *UntypedTensor, inverse bool) error
}
+9 -2
View File
@@ -308,7 +308,13 @@ func (h *cgoSessionHandle) dex() DEXOps { return h.dexOps }
func (h *cgoSessionHandle) createTensor(dtype DType, shape []int) (tensorHandle, error) {
t, err := capi.CreateTensor(h.session, int(dtype), shape)
if err != nil {
return nil, err
// Red CRITICAL Probe 9: translate the capi sentinel into the
// public accel sentinel so consumers using errors.Is against
// accel.ErrInvalidArgument match. Without translateCapiError
// the bare capi.ErrInvalidArgument leaks out and errors.Is
// returns false (distinct errors.New sentinels at distinct
// pointers) → silent CPU fallback in M-1 consumers.
return nil, translateCapiError(err)
}
return &cgoTensorHandle{tensor: t}, nil
}
@@ -316,7 +322,8 @@ func (h *cgoSessionHandle) createTensor(dtype DType, shape []int) (tensorHandle,
func (h *cgoSessionHandle) createTensorWithData(dtype DType, shape []int, data []byte) (tensorHandle, error) {
t, err := capi.CreateTensorWithData(h.session, int(dtype), shape, data)
if err != nil {
return nil, err
// Red CRITICAL Probe 9: see createTensor above.
return nil, translateCapiError(err)
}
return &cgoTensorHandle{tensor: t}, nil
}
+2 -2
View File
@@ -78,8 +78,8 @@ type VMSession struct {
memCap int64
// Underlying GPU session. May be shared (pointer to default) or owned.
sess *Session
owned bool
sess *Session
owned bool
// Serialized dispatch queue. Each op runs while holding qMu, guaranteeing
// FIFO ordering within this VMSession.
+2 -2
View File
@@ -74,8 +74,8 @@ REPLICATE_AGE_RECIPIENTS_FALLBACK=age1pq1... # comma-separated, multi-recipi
### E. Operator CRD
```yaml
apiVersion: lux.network/v1
kind: ReplicateConfig
apiVersion: liquidity.io/v1
kind: LiquidReplicate
spec:
encryption:
kem: xwing # enum: hpke-mlkem768x25519 | xwing
+23 -20
View File
@@ -1,32 +1,35 @@
__pycache__/
.DS_Store
.next/
.pnpm-store/
*.log
**/__pycache__/
**/dist/
**/node_modules/
**/target/
# hygiene (untrack node_modules, block common build output)
# mlkem dudect build artifacts
AGENTS.md
build/
CLAUDE.md
coverage/
dist/
GEMINI.md
GROK.md
QWEN.md
grpc-server.log
# hygiene (untrack node_modules, block common build output)
node_modules/
**/node_modules/
.pnpm-store/
dist/
build/
.next/
coverage/
playwright-report/
test-results/
*.log
tmp/
.DS_Store
# mlkem dudect build artifacts
mlkem/ct/dudect/dudect/
mlkem/ct/dudect/dudect_decaps
mlkem/ct/dudect/dudect_encaps
mlkem/ct/dudect/dudect_keygen
mlkem/ct/dudect/dudect/
mlkem/ct/dudect/libmlkem_*.dylib
mlkem/ct/dudect/libmlkem_*.so
mlkem/ct/dudect/libmlkem_*.h
mlkem/ct/dudect/libmlkem_*.so
mlkem/ct/dudect/results/
node_modules/
playwright-report/
QWEN.md
target/
test-results/
tmp/
*.eco
+15 -1
View File
@@ -1,6 +1,6 @@
# Lux Post-Quantum Cryptography Makefile
.PHONY: all test bench clean fmt lint install-deps verify build
.PHONY: all test bench clean fmt lint install-deps verify build gen_kats
# Go parameters
GOCMD=go
@@ -86,6 +86,20 @@ clean:
rm -f coverage.out
@echo "✅ Clean complete"
# Regenerate KAT vector files for crypto/pq/mldsa.
#
# The generator is deterministic: a second run produces byte-identical
# output. The kats package itself has a TestRegen_Deterministic guard
# that asserts the checked-in vectors_mldsa{44,65,87}.go files match a
# fresh `go run`. After running this target, commit the regenerated
# files; the test suite then proves they round-trip.
gen_kats:
@echo "Regenerating ML-DSA KAT vectors..."
GOWORK=off $(GOCMD) run ./pq/mldsa/kats/internal/gen -out pq/mldsa/kats
@echo "Validating regenerated vectors..."
GOWORK=off $(GOTEST) -count=1 ./pq/mldsa/kats/...
@echo "✅ KAT vectors regenerated and validated"
# Install CI tools
install-tools:
@echo "🛠️ Installing CI tools..."
+87 -33
View File
@@ -145,6 +145,23 @@ func PublicKeyToCompressedBytes(pk *PublicKey) []byte {
}
func PublicKeyFromCompressedBytes(pkBytes []byte) (*PublicKey, error) {
if len(pkBytes) != PublicKeyLen {
return nil, ErrFailedPublicKeyDecompress
}
// HIGH-1: reject the malformed "infinity bit set, compression bit clear"
// encoding (top byte b[0]&0xC0 == 0x40) BEFORE handing the buffer to CIRCL's
// SetBytes. In that branch SetBytes treats the input as UNCOMPRESSED
// (length G1Size=96) and slices b[1:96] on this canonical 48-byte compressed
// buffer → slice-bounds-out-of-range PANIC (ecc/bls12381/g1.go:53). On a
// CGO_ENABLED=0 (purego) node — the canonical image — that is an
// unauthenticated, consensus-halting DoS via any PoP / peer-handshake / warp
// BLS field. The CGO/blst path returns an error for this input (never
// panics); rejecting it here in-band restores parity. The canonical
// compressed-infinity form (0xc0) falls through to Validate() below, which
// already rejects the identity point — so this guard is additive.
if pkBytes[0]&0xC0 == 0x40 {
return nil, ErrFailedPublicKeyDecompress
}
pk := new(blssign.PublicKey[blssign.KeyG1SigG2])
if err := pk.UnmarshalBinary(pkBytes); err != nil {
return nil, ErrFailedPublicKeyDecompress
@@ -161,7 +178,16 @@ func PublicKeyToUncompressedBytes(key *PublicKey) []byte {
func PublicKeyFromValidUncompressedBytes(pkBytes []byte) *PublicKey {
pk := new(blssign.PublicKey[blssign.KeyG1SigG2])
_ = pk.UnmarshalBinary(pkBytes)
if err := pk.UnmarshalBinary(pkBytes); err != nil {
return nil
}
// Identity rejection lives at the ONE deserialization boundary (RESIDUAL C):
// Validate() = !IsIdentity() && IsOnG1(), so an identity (or off-curve) key
// never escapes a constructor. Downstream Verify therefore does NOT re-check
// for the identity point — the check belongs here, once.
if !pk.Validate() {
return nil
}
return &PublicKey{pk: pk}
}
@@ -192,36 +218,32 @@ func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) {
if err := result.UnmarshalBinary(agg.BytesCompressed()); err != nil {
return nil, ErrFailedPublicKeyAggregation
}
// Defence in depth: reject an aggregate that is the IDENTITY (point at
// infinity). Each INPUT key is a valid non-identity subgroup point (the
// constructors call Validate), and the sum of subgroup points stays on-curve and
// in-subgroup — but it can still be the identity when the inputs sum to zero (the
// canonical rogue-key shape: pk + (-pk) = O). An identity aggregate public key
// makes Verify trivially accept the identity signature (a forgery enabler).
// Proof-of-possession at registration already prevents an attacker contributing a
// key it cannot produce, but the verifier must NOT depend on that being enforced
// everywhere: Validate() = !IsIdentity() && IsOnG1() fails the aggregate closed.
if !result.Validate() {
return nil, ErrFailedPublicKeyAggregation
}
return &PublicKey{pk: result}, nil
}
// isIdentityG1 checks if a public key is the identity point (point at infinity).
// Returns true if the key is the identity, false otherwise.
func isIdentityG1(pk *blssign.PublicKey[blssign.KeyG1SigG2]) bool {
// Serialize the public key and check if it's all zeros (compressed identity)
pkBytes, err := pk.MarshalBinary()
if err != nil {
return false
}
// BLS12-381 G1 compressed identity point is a specific encoding
// Check if it matches the identity point encoding
for _, b := range pkBytes {
if b != 0 {
return false
}
}
return true
}
func Verify(pk *PublicKey, sig *Signature, msg []byte) bool {
if pk == nil || pk.pk == nil || sig == nil {
return false
}
// Check that public key is not the identity point (zero-key)
// Identity point verification would trivially pass for any signature
if isIdentityG1(pk.pk) {
return false
}
// The identity (zero) public key is rejected at the deserialization boundary
// (PublicKeyFromCompressedBytes / PublicKeyFromValidUncompressedBytes call
// Validate() = !IsIdentity() && IsOnG1()), so a *PublicKey reaching Verify is
// already a valid non-identity G1 point. Re-checking here was redundant — and
// the old all-zero byte test was WRONG anyway (canonical compressed-G1
// infinity is 0xc0||zeros, not 0x00||zeros) — so it is removed (RESIDUAL C):
// identity rejection belongs at decode, in one place.
return blssign.Verify(pk.pk, msg, sig.sig)
}
@@ -231,10 +253,7 @@ func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool {
if pk == nil || pk.pk == nil || sig == nil {
return false
}
// Check that public key is not the identity point (zero-key)
if isIdentityG1(pk.pk) {
return false
}
// Identity (zero) pubkey already rejected at decode (Validate); see Verify.
// Parse the signature as a G2 point
var sigPoint bls12381.G2
@@ -287,12 +306,47 @@ func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
if len(sigBytes) != SignatureLen {
return nil, ErrFailedSignatureDecompress
}
for _, b := range sigBytes {
if b != 0 {
return &Signature{sig: sigBytes}, nil
}
// HIGH-1: reject the malformed "infinity bit set, compression bit clear"
// encoding (top byte b[0]&0xC0 == 0x40) BEFORE SetBytes. In that branch
// CIRCL treats the input as UNCOMPRESSED (length G2Size=192) and slices
// b[1:192] on this canonical 96-byte compressed buffer → slice-bounds-out-of-
// range PANIC (ecc/bls12381/g2.go:53). On a CGO_ENABLED=0 (purego) node that
// is an unauthenticated, consensus-halting DoS via any peer/warp/quasar BLS
// signature field. blst's Uncompress returns an error for this input (never
// panics); rejecting it here in-band restores parity. The canonical
// compressed-infinity form (0xc0) falls through to the IsIdentity() check
// below — so this guard is additive, not a replacement.
if sigBytes[0]&0xC0 == 0x40 {
return nil, ErrFailedSignatureDecompress
}
return nil, ErrFailedSignatureDecompress
// Validate that the bytes decode to a point that is on-curve AND in the
// prime-order r-torsion subgroup of G2 — the exact contract the CGO/blst
// path enforces with Uncompress + SigValidate(false). CIRCL's
// bls12381.G2.SetBytes performs both checks: it decodes the compressed
// point and then calls IsOnG2() (= isValidProjective && isOnCurve &&
// isRTorsion) before returning, rejecting any input that is not a valid
// subgroup element. Without this, a length-96 non-zero blob that is not a
// real signature point would be accepted here (the prior byte-loop only
// rejected all-zero), diverging from blst and admitting garbage signatures
// into the verifier.
var g bls12381.G2
if err := g.SetBytes(sigBytes); err != nil {
return nil, ErrFailedSignatureDecompress
}
// Reject the G2 identity (point at infinity) — symmetric with the pubkey
// identity guard at decode (Validate, INFO-4). CIRCL's SetBytes accepts a well-formed
// infinity encoding (0xc0 || zeros) and returns at the isInfinity branch BEFORE
// IsOnG2, so without this the identity signature would deserialize cleanly; blst
// SigValidate(false) likewise skips the infinity check. The identity sig does
// not forge against a real key, but accepting it is an asymmetry with the pubkey
// path and admits a degenerate point into the verifier — reject it here.
if g.IsIdentity() {
return nil, ErrFailedSignatureDecompress
}
// Store the validated compressed bytes; blssign.Signature is the raw
// compressed form and blssign.Verify re-derives the point internally, so we
// keep the canonical wire bytes (round-trips through SignatureToBytes).
return &Signature{sig: sigBytes}, nil
}
func AggregateSignatures(sigs []*Signature) (*Signature, error) {
+41 -26
View File
@@ -162,7 +162,16 @@ func PublicKeyToUncompressedBytes(key *PublicKey) []byte {
// are valid.
func PublicKeyFromValidUncompressedBytes(pkBytes []byte) *PublicKey {
pk := new(blst.P1Affine)
pk.Deserialize(pkBytes)
if pk.Deserialize(pkBytes) == nil {
return nil
}
// Identity rejection lives at the ONE deserialization boundary (RESIDUAL C):
// KeyValidate() rejects the infinity point (and off-curve / wrong-subgroup),
// so an identity key never escapes a constructor. Downstream Verify therefore
// does NOT re-check for the identity point — the check belongs here, once.
if !pk.KeyValidate() {
return nil
}
return &PublicKey{pk: pk}
}
@@ -183,22 +192,20 @@ func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) {
}
}
return &PublicKey{pk: agg.ToAffine()}, nil
}
// isIdentityG1 checks if a public key is the identity point (point at infinity).
// Returns true if the key is the identity, false otherwise.
func isIdentityG1(pk *blst.P1Affine) bool {
// Check if the point is at infinity using blst's built-in check
// The identity point in G1 is represented as all zeros in compressed form
pkBytes := pk.Compress()
// Check for all-zero bytes (identity point encoding)
for _, b := range pkBytes {
if b != 0 {
return false
}
// Defence in depth: reject an aggregate that is the IDENTITY (point at
// infinity). Each INPUT key is a valid non-identity subgroup point (the
// constructors call KeyValidate), and the sum of subgroup points stays on-curve
// and in-subgroup — but it can still be the identity when the inputs sum to zero
// (the canonical rogue-key shape: pk + (-pk) = O). An identity aggregate public
// key makes Verify trivially accept the identity signature (a forgery enabler).
// blst's KeyValidate rejects the infinity point (and off-curve / wrong-subgroup),
// matching the purego path's result.Validate() — fail the aggregate closed rather
// than depend on proof-of-possession being enforced upstream everywhere.
out := agg.ToAffine()
if !out.KeyValidate() {
return nil, ErrFailedPublicKeyAggregation
}
return true
return &PublicKey{pk: out}, nil
}
// Verify the [sig] of [msg] against the [pk].
@@ -206,11 +213,13 @@ func Verify(pk *PublicKey, sig *Signature, msg []byte) bool {
if pk == nil || pk.pk == nil || sig == nil || sig.sig == nil {
return false
}
// Check that public key is not the identity point (zero-key)
// Identity point verification would trivially pass for any signature
if isIdentityG1(pk.pk) {
return false
}
// The identity (zero) public key is rejected at the deserialization boundary
// (PublicKeyFromCompressedBytes / PublicKeyFromValidUncompressedBytes call
// KeyValidate(), which rejects the infinity point), so a *PublicKey reaching
// Verify is already a valid non-identity G1 point. Re-checking here was
// redundant — and the old all-zero byte test was WRONG anyway (blst Compress()
// of the identity is 0xc0||zeros, not 0x00||zeros, so it never actually
// matched) — removed (RESIDUAL C): identity rejection belongs at decode, once.
return sig.sig.Verify(true, pk.pk, false, msg, dstSignature)
}
@@ -219,10 +228,7 @@ func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool {
if pk == nil || pk.pk == nil || sig == nil || sig.sig == nil {
return false
}
// Check that public key is not the identity point (zero-key)
if isIdentityG1(pk.pk) {
return false
}
// Identity (zero) pubkey already rejected at decode (KeyValidate); see Verify.
return sig.sig.Verify(true, pk.pk, false, msg, dstPoP)
}
@@ -247,7 +253,16 @@ func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
return nil, ErrFailedSignatureDecompress
}
if !sig.SigValidate(false) {
// SigValidate(true) checks BOTH r-torsion subgroup membership AND rejects the
// G2 identity (point at infinity) — go_p2_affine_validate(p, infcheck=true) is
// `if is_inf(p) return false; return in_g2(p)`. The prior SigValidate(false)
// skipped the infinity check, accepting the identity signature (INFO-4); this
// makes the blst path symmetric with the purego SignatureFromBytes identity
// reject and with the pubkey identity guard at decode (KeyValidate). The
// identity sig does not forge
// against a real key, but admitting a degenerate point into the verifier is an
// asymmetry worth closing.
if !sig.SigValidate(true) {
return nil, ErrInvalidSignature
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package hash
import (
// Ethereum Keccak-256 (0x01 domain pad), NOT FIPS-202 SHA3-256 (0x06 pad).
// NewLegacyKeccak256 is the legacy (pre-standardization) Keccak that every
// Lux GPU backend and EVM-compatible consumer hashes against; sha3.Sum256
// would diverge byte-for-byte. Do not swap it.
"golang.org/x/crypto/sha3"
)
// KeccakSize is the byte length of a Keccak-256 digest.
const KeccakSize = 32
// ComputeKeccak256Array computes the Ethereum Keccak-256 hash of the
// concatenation of the input byte slices.
func ComputeKeccak256Array(data ...[]byte) [KeccakSize]byte {
h := sha3.NewLegacyKeccak256()
for _, b := range data {
h.Write(b)
}
var out [KeccakSize]byte
h.Sum(out[:0])
return out
}
// ComputeKeccak256 computes the Ethereum Keccak-256 hash of the concatenation
// of the input byte slices.
func ComputeKeccak256(data ...[]byte) []byte {
out := ComputeKeccak256Array(data...)
return out[:]
}
+3 -3
View File
@@ -16,9 +16,9 @@ import (
)
var (
once sync.Once
sess *accel.Session
initErr error
once sync.Once
sess *accel.Session
initErr error
available bool
)
+31 -11
View File
@@ -26,12 +26,12 @@ var concurrentBatchThreshold = 8
//
// Dispatch ladder:
//
// 1. GPU substrate (accel.LatticeOps.MLDSAVerifyBatch) when n >= BatchThreshold
// AND a backend plugin is loaded AND mode is in {44, 65, 87}.
// 2. Goroutine-parallel CPU verify when n >= concurrentBatchThreshold.
// Scales to GOMAXPROCS without GPU substrate. Useful on hosts that
// have CPU cores but no GPU plugin (Linux CI, no-Metal Macs, etc).
// 3. Serial CPU verify as the floor.
// 1. GPU substrate (accel.LatticeOps.MLDSAVerifyBatch) when n >= BatchThreshold
// AND a backend plugin is loaded AND mode is in {44, 65, 87}.
// 2. Goroutine-parallel CPU verify when n >= concurrentBatchThreshold.
// Scales to GOMAXPROCS without GPU substrate. Useful on hosts that
// have CPU cores but no GPU plugin (Linux CI, no-Metal Macs, etc).
// 3. Serial CPU verify as the floor.
//
// All paths are byte-equal: each path ends in cloudflare/circl's
// FIPS 204-conformant Verify. The dispatch choice only affects throughput,
@@ -56,9 +56,21 @@ func BatchVerify(pubs []*PublicKey, msgs [][]byte, sigs [][]byte) []bool {
}
// Tier 1: GPU substrate.
//
// Red M-1: batchVerifyGPU returns (false, accel.ErrInvalidArgument) when
// the C ABI rejects the input as malformed (length / count cap, null
// ptr). The CPU oracle may accept what GPU rejects → consensus split.
// Panic here — BatchVerify returns no error, and a contract-violating
// input deserves the same shape as the existing length-mismatch /
// mixed-mode panics above. Recoverable GPU errors (NotSupported,
// OutOfMemory, kernel failure) return (false, nil) and fall through.
if n >= BatchThreshold {
if _, ok := modeToCAPI(mode); ok {
if ok, err := batchVerifyGPU(pubs, msgs, sigs, out); ok && err == nil {
ok, err := batchVerifyGPU(pubs, msgs, sigs, out)
if err != nil {
panic("mldsa.BatchVerify: GPU dispatch rejected input as malformed: " + err.Error())
}
if ok {
return out
}
}
@@ -131,9 +143,9 @@ var ErrBatchLength = errors.New("mldsa: batch input slices have inconsistent len
//
// Dispatch ladder mirrors BatchVerify:
//
// 1. GPU substrate (accel.LatticeOps.MLDSASignBatch).
// 2. Goroutine-parallel CPU sign.
// 3. Serial CPU sign.
// 1. GPU substrate (accel.LatticeOps.MLDSASignBatch).
// 2. Goroutine-parallel CPU sign.
// 3. Serial CPU sign.
//
// For deterministic-mode signing (FIPS 204 § Algorithm 2 with rnd=0^256),
// callers can pass nil for randSource. The CPU path passes deterministic=true
@@ -156,9 +168,17 @@ func BatchSign(randSource io.Reader, privs []*PrivateKey, msgs [][]byte) ([][]by
}
// Tier 1: GPU substrate.
//
// Red M-1: batchSignGPU returns (false, accel.ErrInvalidArgument) on
// malformed input. Propagate as a real error here (BatchSign already
// returns error); other GPU errors fall through to CPU silently.
if n >= BatchThreshold {
if _, ok := modeToCAPI(mode); ok {
if ok, err := batchSignGPU(privs, msgs, sigs); ok && err == nil {
ok, err := batchSignGPU(privs, msgs, sigs)
if err != nil {
return nil, err
}
if ok {
return sigs, nil
}
}
+29 -4
View File
@@ -39,6 +39,8 @@
package mldsa
import (
"errors"
"github.com/luxfi/accel"
"github.com/luxfi/crypto/backend"
"github.com/luxfi/crypto/internal/gpuhost"
@@ -66,14 +68,21 @@ func modeToCAPI(m Mode) (int, bool) {
// API enforces this; this entrypoint is internal and trusts the contract).
//
// Returns (true, nil) when the GPU path produced `out`. Returns (false, nil)
// in any of these cases:
// in any of these cases (caller may fall back to CPU):
//
// - CRYPTO_BACKEND is not GPU
// - gpuhost has no accel session
// - the mode is not wired for GPU dispatch
// - the substrate returns NotSupported (no plugin loaded)
// - any tensor allocation or kernel dispatch failed (we fall through
// silently to CPU; the caller's per-element loop is the safety net)
// - tensor allocation failed (OutOfMemory) or kernel dispatch failed
//
// Returns (false, accel.ErrInvalidArgument) when the C ABI rejects the input
// as malformed (msg_len > LUX_GPU_MLDSA_MSG_LEN_CAP, count > UINT32_MAX, null
// pointer with non-zero length). The CPU oracle (cloudflare/circl) may accept
// what the GPU wrapper rejects (FIPS 204 allows arbitrary-length messages),
// so silently falling back would produce a verdict on one backend and not the
// other across the same input — consensus split. Red M-1 contract: callers
// MUST propagate ErrInvalidArgument as a hard error, never silently fall back.
//
// `out` is only written when the function returns (true, nil).
func batchVerifyGPU(pubs []*PublicKey, msgs [][]byte, sigs [][]byte, out []bool) (bool, error) {
@@ -160,11 +169,22 @@ func batchVerifyGPU(pubs []*PublicKey, msgs [][]byte, sigs [][]byte, out []bool)
// Try the new mode-aware MLDSAVerifyBatch first. When ML-DSA-65 is in use
// fall back to the legacy DilithiumVerifyBatch for substrates that only
// publish the Dilithium3 kernel (the original wiring).
//
// Red M-1: ErrInvalidArgument is a HARD error. The GPU wrapper rejected
// the input as violating the contract (length cap, count cap, null with
// non-zero len). The CPU oracle may accept what the GPU wrapper rejects
// → consensus split between CPU-only and GPU-accelerated verifiers. Do
// not silently fall back; surface the error so the caller can panic /
// reject the batch. Other errors (NotSupported, OutOfMemory, kernel
// failure) are recoverable — CPU fallback is correct for those.
dispatchErr := sess.Lattice().MLDSAVerifyBatch(capiMode, mT.Untyped(), sT.Untyped(), pT.Untyped(), rT.Untyped())
if dispatchErr != nil && mode == MLDSA65 {
if dispatchErr != nil && mode == MLDSA65 && !errors.Is(dispatchErr, accel.ErrInvalidArgument) {
dispatchErr = sess.Lattice().DilithiumVerifyBatch(mT.Untyped(), sT.Untyped(), pT.Untyped(), rT.Untyped())
}
if dispatchErr != nil {
if errors.Is(dispatchErr, accel.ErrInvalidArgument) {
return false, dispatchErr
}
return false, nil
}
bytes, err := rT.ToSlice()
@@ -257,7 +277,12 @@ func batchSignGPU(privs []*PrivateKey, msgs [][]byte, sigs [][]byte) (bool, erro
}
defer sigT.Close()
// Red M-1: ErrInvalidArgument is a HARD error (see batchVerifyGPU). Other
// errors silently fall back to CPU.
if err := sess.Lattice().MLDSASignBatch(capiMode, mT.Untyped(), skT.Untyped(), sigT.Untyped()); err != nil {
if errors.Is(err, accel.ErrInvalidArgument) {
return false, err
}
return false, nil
}
+6 -6
View File
@@ -50,12 +50,12 @@ func (t DispatchTier) String() string {
// Provenance is the auditable evidence record for the ML-DSA package.
// See slhdsa.Provenance — same shape, same purpose.
type Provenance struct {
Tier DispatchTier
AccelInitialised bool
DeviceAvailable bool
PluginStrongSymbol bool
BatchThresholdN int
ConcurrentThresholdN int
Tier DispatchTier
AccelInitialised bool
DeviceAvailable bool
PluginStrongSymbol bool
BatchThresholdN int
ConcurrentThresholdN int
}
// GetProvenance returns the current dispatch provenance.
+1 -1
View File
@@ -15,7 +15,7 @@ require (
github.com/stretchr/testify v1.9.0
github.com/luxfi/geth v1.16.34
github.com/luxfi/lattice/v6 v6.1.1
github.com/luxfi/corona v0.1.0
github.com/luxfi/corona v0.7.6
)
replace (
+11
View File
@@ -0,0 +1,11 @@
geth (luxfi/geth)
Copyright (c) 2020-2025 Lux Industries Inc.
This product includes software from go-ethereum (https://github.com/ethereum/go-ethereum), licensed under LGPL-3.0:
Copyright 2013-2025 The go-ethereum Authors
The go-ethereum library files retain their original GNU Lesser General Public
License v3.0 (see COPYING.LESSER); the standalone command-line binaries retain
the GNU General Public License v3.0 (see COPYING). Lux modifications are
contributed under the same terms.
+6 -5
View File
@@ -1,12 +1,13 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// zap.go — load a BIP-39 mnemonic from KMS over native ZAP.
// zap.go — load a BIP-39 mnemonic from Liquid KMS over native ZAP.
//
// This file is the one canonical path every Lux-derived service uses
// to resolve the bootstrap mnemonic: luxd, netrunner, lux/cli, and any
// descending L1's bootstrap. No file mount, no projected volume, no
// per-consumer copy of the env-vs-KMS precedence chain.
// descending L1's bootstrap (liquidity/network-bootstrap, hanzo-bootstrap,
// zoo-bootstrap, …). No file mount, no projected volume, no per-consumer
// copy of the env-vs-KMS precedence chain.
//
// Separation of concerns:
//
@@ -20,7 +21,7 @@
// Precedence (env wins; KMS is the production fallback):
//
// 1. MNEMONIC env var local dev + CI test seam
// 2. KMS at (addr, env, path) native ZAP from KMS
// 2. KMS at (addr, env, path) native ZAP from Liquid KMS
package keys
@@ -74,7 +75,7 @@ var dialKMS = func(ctx context.Context, addr string, identity *ServiceIdentity)
// `addr` and reads the secret at `path` under `env`.
//
// ctx cancellable context
// addr KMS host:port (e.g. "kms.default.svc:9999")
// addr KMS host:port (e.g. "kms.internal.svc:9999")
// env KMS env scope ("mainnet" | "testnet" | "devnet")
// path KMS secret path (e.g. "/mnemonic" or "/foo/master")
// identity ServiceIdentity to sign the secret-opcode envelope. May
+29
View File
@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2024, Lux Partners Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+12
View File
@@ -0,0 +1,12 @@
# Licensing
This repository is licensed under the **BSD 3-Clause License** (see
[LICENSE](LICENSE)). It belongs to the **public** tier of the Lux
three-tier IP strategy: anyone may use, fork, or redistribute it,
including for commercial purposes, subject to the BSD-3 terms.
For the canonical Lux IP and licensing strategy, see:
<https://github.com/luxfi/.github/blob/main/profile/README.md>
For commercial inquiries that go beyond BSD-3 (e.g. private moat
acceleration kernels), contact `licensing@lux.network`.
+37
View File
@@ -0,0 +1,37 @@
# LLM.md - Hanzo Log
## Overview
Go module: github.com/luxfi/log
## Tech Stack
- **Language**: Go
## Build & Run
```bash
go build ./...
go test ./...
```
## Structure
```
log/
LICENSE
README.md
array.go
benchmark_test.go
console.go
context.go
ctx.go
encoder.go
encoder_json.go
error_marshal_test.go
event.go
factory.go
fields.go
fixtures_test.go
geth.go
```
## Key Files
- `README.md` -- Project documentation
- `go.mod` -- Go module definition
+4 -7
View File
@@ -101,9 +101,9 @@ type ConsoleWriter struct {
// NewConsoleWriter creates and initializes a new ConsoleWriter.
func NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter {
w := ConsoleWriter{
Out: os.Stdout,
TimeFormat: consoleDefaultTimeFormat,
PartsOrder: consoleDefaultPartsOrder(),
Out: os.Stdout,
TimeFormat: consoleDefaultTimeFormat,
PartsOrder: consoleDefaultPartsOrder(),
}
for _, opt := range options {
@@ -120,10 +120,7 @@ func NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter {
// Write transforms the JSON input with formatters and appends to w.Out.
func (w ConsoleWriter) Write(p []byte) (n int, err error) {
// Fix color on Windows
if w.Out == os.Stdout || w.Out == os.Stderr {
w.Out = colorable.NewColorable(w.Out.(*os.File))
}
// Note: colorable wrapping is done once in NewConsoleWriter, not on every Write
if w.PartsOrder == nil {
w.PartsOrder = consoleDefaultPartsOrder()
+4 -1
View File
@@ -8,7 +8,10 @@ import (
var disabledLogger Logger
func init() {
SetGlobalLevel(TraceLevel)
// Global level defaults to DebugLevel (zero value of Level = 0).
// Applications that want trace output must call SetGlobalLevel(TraceLevel).
// Do NOT reset to TraceLevel here -- it causes debug spam from dependency
// init() functions before the application configures its own log level.
disabledLogger = Noop()
}
+43 -37
View File
@@ -87,7 +87,8 @@ type factory struct {
// factoryLogger wraps a Logger with factory-managed level controls.
type factoryLogger struct {
Logger
Logger // current logger with level applied
baseLogger Logger // base logger without level (for SetLogLevel)
level Level
displayLevel Level
name string
@@ -120,7 +121,7 @@ func (f *factory) Make(name string) (Logger, error) {
return Noop(), nil
}
// Return existing logger if already created
// Return existing logger if already created (reflects any SetLogLevel changes)
if l, exists := f.loggers[name]; exists {
return l.Logger, nil
}
@@ -179,12 +180,13 @@ func (f *factory) Make(name string) (Logger, error) {
w = io.MultiWriter(writers...)
}
// Create logger
logger := NewWriter(w).With().Timestamp().Str("logger", name).Logger()
logger = logger.Level(f.config.LogLevel)
// Create logger - store base logger separately so SetLogLevel can recreate with new level
baseLogger := NewWriter(w).With().Timestamp().Str("logger", name).Logger()
logger := baseLogger.Level(f.config.LogLevel)
fl := &factoryLogger{
Logger: logger,
baseLogger: baseLogger,
level: f.config.LogLevel,
displayLevel: f.config.DisplayLevel,
name: name,
@@ -192,7 +194,7 @@ func (f *factory) Make(name string) (Logger, error) {
}
f.loggers[name] = fl
return logger, nil
return fl.Logger, nil
}
// MakeChain creates a logger for a blockchain.
@@ -220,6 +222,10 @@ func (f *factory) SetLogLevel(name string, level Level) {
if l, exists := f.loggers[name]; exists {
l.level = level
// Recreate logger with new level using the base logger
if l.baseLogger != nil {
l.Logger = l.baseLogger.Level(level)
}
}
}
@@ -269,37 +275,37 @@ func (f *factory) Close() {
// NoLog is a no-op logger for use in tests or when logging is disabled.
type NoLog struct{}
func (NoLog) Output(io.Writer) Logger { return Noop() }
func (NoLog) With() Context { return Context{} }
func (NoLog) Level(Level) Logger { return Noop() }
func (NoLog) GetLevel() Level { return Disabled }
func (NoLog) New(...interface{}) Logger { return Noop() }
func (NoLog) Sample(Sampler) Logger { return Noop() }
func (NoLog) Hook(...Hook) Logger { return Noop() }
func (NoLog) Trace(string, ...interface{}) {}
func (NoLog) Debug(string, ...interface{}) {}
func (NoLog) Info(string, ...interface{}) {}
func (NoLog) Warn(string, ...interface{}) {}
func (NoLog) Error(string, ...interface{}) {}
func (NoLog) Fatal(string, ...interface{}) {}
func (NoLog) Panic(string, ...interface{}) {}
func (NoLog) Crit(string, ...interface{}) {}
func (NoLog) Verbo(string, ...interface{}) {}
func (NoLog) Output(io.Writer) Logger { return Noop() }
func (NoLog) With() Context { return Context{} }
func (NoLog) Level(Level) Logger { return Noop() }
func (NoLog) GetLevel() Level { return Disabled }
func (NoLog) New(...interface{}) Logger { return Noop() }
func (NoLog) Sample(Sampler) Logger { return Noop() }
func (NoLog) Hook(...Hook) Logger { return Noop() }
func (NoLog) Trace(string, ...interface{}) {}
func (NoLog) Debug(string, ...interface{}) {}
func (NoLog) Info(string, ...interface{}) {}
func (NoLog) Warn(string, ...interface{}) {}
func (NoLog) Error(string, ...interface{}) {}
func (NoLog) Fatal(string, ...interface{}) {}
func (NoLog) Panic(string, ...interface{}) {}
func (NoLog) Crit(string, ...interface{}) {}
func (NoLog) Verbo(string, ...interface{}) {}
func (NoLog) Log(Level, string, ...interface{}) {}
func (NoLog) TraceEvent() *Event { return nil }
func (NoLog) DebugEvent() *Event { return nil }
func (NoLog) InfoEvent() *Event { return nil }
func (NoLog) WarnEvent() *Event { return nil }
func (NoLog) ErrorEvent() *Event { return nil }
func (NoLog) FatalEvent() *Event { return nil }
func (NoLog) PanicEvent() *Event { return nil }
func (NoLog) Err(error) *Event { return nil }
func (NoLog) WithLevel(Level) *Event { return nil }
func (NoLog) LogEvent() *Event { return nil }
func (NoLog) Print(...interface{}) {}
func (NoLog) Printf(string, ...interface{}) {}
func (NoLog) Write(p []byte) (int, error) { return len(p), nil }
func (NoLog) SetLogLevel(string) error { return nil }
func (NoLog) TraceEvent() *Event { return nil }
func (NoLog) DebugEvent() *Event { return nil }
func (NoLog) InfoEvent() *Event { return nil }
func (NoLog) WarnEvent() *Event { return nil }
func (NoLog) ErrorEvent() *Event { return nil }
func (NoLog) FatalEvent() *Event { return nil }
func (NoLog) PanicEvent() *Event { return nil }
func (NoLog) Err(error) *Event { return nil }
func (NoLog) WithLevel(Level) *Event { return nil }
func (NoLog) LogEvent() *Event { return nil }
func (NoLog) Print(...interface{}) {}
func (NoLog) Printf(string, ...interface{}) {}
func (NoLog) Write(p []byte) (int, error) { return len(p), nil }
func (NoLog) SetLogLevel(string) error { return nil }
func (NoLog) RecoverAndPanic(fn func()) {
defer func() {
if r := recover(); r != nil {
@@ -308,7 +314,7 @@ func (NoLog) RecoverAndPanic(fn func()) {
}()
fn()
}
func (NoLog) IsZero() bool { return true }
func (NoLog) IsZero() bool { return true }
func (NoLog) Enabled(context.Context, slog.Level) bool { return false }
// ToLevel parses a level string and returns the corresponding Level.
+41 -35
View File
@@ -6,6 +6,7 @@ package log
import (
"fmt"
"os"
"runtime"
"strconv"
"time"
)
@@ -26,51 +27,57 @@ type Field struct {
}
// Field constructors for geth-style logging
func String(key, val string) Field { return Field{Key: key, Value: val} }
func Stringer(key string, val fmt.Stringer) Field { return Field{Key: key, Value: val} }
func Int(key string, val int) Field { return Field{Key: key, Value: val} }
func Int8(key string, val int8) Field { return Field{Key: key, Value: val} }
func Int16(key string, val int16) Field { return Field{Key: key, Value: val} }
func Int32(key string, val int32) Field { return Field{Key: key, Value: val} }
func Int64(key string, val int64) Field { return Field{Key: key, Value: val} }
func Uint(key string, val uint) Field { return Field{Key: key, Value: val} }
func Uint8(key string, val uint8) Field { return Field{Key: key, Value: val} }
func Uint16(key string, val uint16) Field { return Field{Key: key, Value: val} }
func Uint32(key string, val uint32) Field { return Field{Key: key, Value: val} }
func Uint64(key string, val uint64) Field { return Field{Key: key, Value: val} }
func Float32(key string, val float32) Field { return Field{Key: key, Value: val} }
func Float64(key string, val float64) Field { return Field{Key: key, Value: val} }
func Bool(key string, val bool) Field { return Field{Key: key, Value: val} }
func String(key, val string) Field { return Field{Key: key, Value: val} }
func Stringer(key string, val fmt.Stringer) Field { return Field{Key: key, Value: val} }
func Int(key string, val int) Field { return Field{Key: key, Value: val} }
func Int8(key string, val int8) Field { return Field{Key: key, Value: val} }
func Int16(key string, val int16) Field { return Field{Key: key, Value: val} }
func Int32(key string, val int32) Field { return Field{Key: key, Value: val} }
func Int64(key string, val int64) Field { return Field{Key: key, Value: val} }
func Uint(key string, val uint) Field { return Field{Key: key, Value: val} }
func Uint8(key string, val uint8) Field { return Field{Key: key, Value: val} }
func Uint16(key string, val uint16) Field { return Field{Key: key, Value: val} }
func Uint32(key string, val uint32) Field { return Field{Key: key, Value: val} }
func Uint64(key string, val uint64) Field { return Field{Key: key, Value: val} }
func Float32(key string, val float32) Field { return Field{Key: key, Value: val} }
func Float64(key string, val float64) Field { return Field{Key: key, Value: val} }
func Bool(key string, val bool) Field { return Field{Key: key, Value: val} }
func Duration(key string, val time.Duration) Field { return Field{Key: key, Value: val} }
func Time(key string, val time.Time) Field { return Field{Key: key, Value: val} }
func Err(err error) Field { return Field{Key: ErrorFieldName, Value: err} }
func NamedErr(key string, err error) Field { return Field{Key: key, Value: err} }
func Any(key string, val interface{}) Field { return Field{Key: key, Value: val} }
func Binary(key string, val []byte) Field { return Field{Key: key, Value: val} }
func ByteString(key string, val []byte) Field { return Field{Key: key, Value: string(val)} }
func Time(key string, val time.Time) Field { return Field{Key: key, Value: val} }
func Err(err error) Field { return Field{Key: ErrorFieldName, Value: err} }
func NamedErr(key string, err error) Field { return Field{Key: key, Value: err} }
func Any(key string, val interface{}) Field { return Field{Key: key, Value: val} }
func Binary(key string, val []byte) Field { return Field{Key: key, Value: val} }
func ByteString(key string, val []byte) Field { return Field{Key: key, Value: string(val)} }
// Slice field constructors
func Strings(key string, val []string) Field { return Field{Key: key, Value: val} }
func Ints(key string, val []int) Field { return Field{Key: key, Value: val} }
func Int64s(key string, val []int64) Field { return Field{Key: key, Value: val} }
func Uint64s(key string, val []uint64) Field { return Field{Key: key, Value: val} }
func Float64s(key string, val []float64) Field { return Field{Key: key, Value: val} }
func Bools(key string, val []bool) Field { return Field{Key: key, Value: val} }
func Strings(key string, val []string) Field { return Field{Key: key, Value: val} }
func Ints(key string, val []int) Field { return Field{Key: key, Value: val} }
func Int64s(key string, val []int64) Field { return Field{Key: key, Value: val} }
func Uint64s(key string, val []uint64) Field { return Field{Key: key, Value: val} }
func Float64s(key string, val []float64) Field { return Field{Key: key, Value: val} }
func Bools(key string, val []bool) Field { return Field{Key: key, Value: val} }
func Durations(key string, val []time.Duration) Field { return Field{Key: key, Value: val} }
func Times(key string, val []time.Time) Field { return Field{Key: key, Value: val} }
func Times(key string, val []time.Time) Field { return Field{Key: key, Value: val} }
// Short-form aliases (matching chaining API style)
func Str(key, val string) Field { return String(key, val) }
func Dur(key string, val time.Duration) Field { return Duration(key, val) }
func AnErr(key string, err error) Field { return NamedErr(key, err) }
func Str(key, val string) Field { return String(key, val) }
func Dur(key string, val time.Duration) Field { return Duration(key, val) }
func AnErr(key string, err error) Field { return NamedErr(key, err) }
// Stack returns a Field with the current stack trace.
func Stack(key string) Field {
return Field{Key: key, Value: "stack"} // Placeholder, actual stack in event
buf := make([]byte, 4096)
n := runtime.Stack(buf, false)
return Field{Key: key, Value: string(buf[:n])}
}
// defaultLogger is the global logger for geth-style functions
var defaultLogger = NewWriter(os.Stderr).With().Timestamp().Logger()
// defaultLogger is the global logger for geth-style functions.
// Default level is Info — debug/trace messages are suppressed unless
// explicitly enabled via SetDefault() or SetGlobalLevel(DebugLevel).
// This prevents dependency init() functions from spamming stderr
// with debug output before the application configures logging.
var defaultLogger = NewWriter(os.Stderr).With().Timestamp().Logger().Level(InfoLevel)
// SetDefault sets the default logger for geth-style functions
func SetDefault(l Logger) {
@@ -268,4 +275,3 @@ func NewTestLogger(level ...Level) Logger {
}
return l
}
+1
View File
@@ -1,3 +1,4 @@
//go:build go1.12
// +build go1.12
package log
+33 -33
View File
@@ -593,39 +593,39 @@ func (l *logger) should(lvl Level) bool {
type noopLogger struct{}
func (noopLogger) Trace(string, ...interface{}) {}
func (noopLogger) Debug(string, ...interface{}) {}
func (noopLogger) Info(string, ...interface{}) {}
func (noopLogger) Warn(string, ...interface{}) {}
func (noopLogger) Error(string, ...interface{}) {}
func (noopLogger) Fatal(string, ...interface{}) {}
func (noopLogger) Panic(string, ...interface{}) {}
func (noopLogger) Crit(string, ...interface{}) {}
func (noopLogger) Verbo(string, ...interface{}) {}
func (noopLogger) Log(Level, string, ...interface{}) {}
func (n noopLogger) With() Context { return Context{} }
func (n noopLogger) New(...interface{}) Logger { return n }
func (n noopLogger) Output(io.Writer) Logger { return n }
func (n noopLogger) Level(Level) Logger { return n }
func (noopLogger) GetLevel() Level { return Disabled }
func (noopLogger) IsZero() bool { return true }
func (noopLogger) Enabled(context.Context, slog.Level) bool { return false }
func (noopLogger) Sample(Sampler) Logger { return Noop() }
func (noopLogger) Hook(...Hook) Logger { return Noop() }
func (noopLogger) TraceEvent() *Event { return nil }
func (noopLogger) DebugEvent() *Event { return nil }
func (noopLogger) InfoEvent() *Event { return nil }
func (noopLogger) WarnEvent() *Event { return nil }
func (noopLogger) ErrorEvent() *Event { return nil }
func (noopLogger) FatalEvent() *Event { return nil }
func (noopLogger) PanicEvent() *Event { return nil }
func (noopLogger) Err(error) *Event { return nil }
func (noopLogger) WithLevel(Level) *Event { return nil }
func (noopLogger) LogEvent() *Event { return nil }
func (noopLogger) Print(...interface{}) {}
func (noopLogger) Printf(string, ...interface{}) {}
func (noopLogger) Write(p []byte) (int, error) { return len(p), nil }
func (noopLogger) SetLogLevel(string) error { return nil }
func (noopLogger) Trace(string, ...interface{}) {}
func (noopLogger) Debug(string, ...interface{}) {}
func (noopLogger) Info(string, ...interface{}) {}
func (noopLogger) Warn(string, ...interface{}) {}
func (noopLogger) Error(string, ...interface{}) {}
func (noopLogger) Fatal(string, ...interface{}) {}
func (noopLogger) Panic(string, ...interface{}) {}
func (noopLogger) Crit(string, ...interface{}) {}
func (noopLogger) Verbo(string, ...interface{}) {}
func (noopLogger) Log(Level, string, ...interface{}) {}
func (n noopLogger) With() Context { return Context{} }
func (n noopLogger) New(...interface{}) Logger { return n }
func (n noopLogger) Output(io.Writer) Logger { return n }
func (n noopLogger) Level(Level) Logger { return n }
func (noopLogger) GetLevel() Level { return Disabled }
func (noopLogger) IsZero() bool { return true }
func (noopLogger) Enabled(context.Context, slog.Level) bool { return false }
func (noopLogger) Sample(Sampler) Logger { return Noop() }
func (noopLogger) Hook(...Hook) Logger { return Noop() }
func (noopLogger) TraceEvent() *Event { return nil }
func (noopLogger) DebugEvent() *Event { return nil }
func (noopLogger) InfoEvent() *Event { return nil }
func (noopLogger) WarnEvent() *Event { return nil }
func (noopLogger) ErrorEvent() *Event { return nil }
func (noopLogger) FatalEvent() *Event { return nil }
func (noopLogger) PanicEvent() *Event { return nil }
func (noopLogger) Err(error) *Event { return nil }
func (noopLogger) WithLevel(Level) *Event { return nil }
func (noopLogger) LogEvent() *Event { return nil }
func (noopLogger) Print(...interface{}) {}
func (noopLogger) Printf(string, ...interface{}) {}
func (noopLogger) Write(p []byte) (int, error) { return len(p), nil }
func (noopLogger) SetLogLevel(string) error { return nil }
func (noopLogger) RecoverAndPanic(fn func()) {
defer func() {
if r := recover(); r != nil {
+1
View File
@@ -1,3 +1,4 @@
//go:build !go1.12
// +build !go1.12
package log
+170 -2
View File
@@ -1,9 +1,14 @@
package log
import (
"math/rand"
"hash/fnv"
"sync"
"sync/atomic"
"time"
crand "crypto/rand"
"encoding/binary"
"math/rand/v2"
)
var (
@@ -13,8 +18,30 @@ var (
Sometimes = RandomSampler(100)
// Rarely samples log every ~ 1000 events.
Rarely = RandomSampler(1000)
// globalRand is a thread-safe random source
globalRand = newLockedRand()
)
// lockedRand is a thread-safe random number generator
type lockedRand struct {
mu sync.Mutex
rng *rand.Rand
}
func newLockedRand() *lockedRand {
var seed [8]byte
_, _ = crand.Read(seed[:])
src := rand.NewPCG(binary.LittleEndian.Uint64(seed[:]), binary.LittleEndian.Uint64(seed[:]))
return &lockedRand{rng: rand.New(src)}
}
func (r *lockedRand) Intn(n int) int {
r.mu.Lock()
defer r.mu.Unlock()
return r.rng.IntN(n)
}
// Sampler defines an interface to a log sampler.
type Sampler interface {
// Sample returns true if the event should be part of the sample, false if
@@ -31,7 +58,7 @@ func (s RandomSampler) Sample(lvl Level) bool {
if s <= 0 {
return false
}
if rand.Intn(int(s)) != 0 {
if globalRand.Intn(int(s)) != 0 {
return false
}
return true
@@ -135,3 +162,144 @@ func (s LevelSampler) Sample(lvl Level) bool {
}
return true
}
// DedupSampler suppresses duplicate log messages within a time window.
// After the window expires, it emits a summary of suppressed messages.
type DedupSampler struct {
// Window is the deduplication time window. Defaults to 1 minute.
Window time.Duration
// MaxKeys is the maximum number of unique messages to track. Defaults to 1000.
MaxKeys int
mu sync.Mutex
seen map[uint64]*dedupEntry
cleanup int64 // next cleanup time (unix nano)
}
type dedupEntry struct {
count int64
firstAt int64
lastAt int64
lastEmit int64
}
// NewDedupSampler creates a dedup sampler with the given window.
func NewDedupSampler(window time.Duration) *DedupSampler {
if window <= 0 {
window = time.Minute
}
return &DedupSampler{
Window: window,
MaxKeys: 1000,
seen: make(map[uint64]*dedupEntry),
}
}
// SampleMsg returns true if this message should be logged.
// It tracks message hashes and suppresses duplicates within the window.
func (s *DedupSampler) SampleMsg(lvl Level, msg string) bool {
return s.SampleMsgWithKey(lvl, msg, "")
}
// SampleMsgWithKey allows specifying a custom dedup key instead of using msg.
func (s *DedupSampler) SampleMsgWithKey(lvl Level, msg, key string) bool {
if key == "" {
key = msg
}
// Hash the level + message for dedup key
h := fnv.New64a()
h.Write([]byte{byte(lvl)})
h.Write([]byte(key))
hash := h.Sum64()
now := TimestampFunc().UnixNano()
windowNano := s.Window.Nanoseconds()
s.mu.Lock()
defer s.mu.Unlock()
// Lazy init
if s.seen == nil {
s.seen = make(map[uint64]*dedupEntry)
}
// Periodic cleanup of old entries
if now > s.cleanup {
s.cleanupLocked(now, windowNano)
s.cleanup = now + windowNano
}
entry, exists := s.seen[hash]
if !exists {
// First time seeing this message
if len(s.seen) >= s.MaxKeys {
// Evict oldest entry if at capacity
s.evictOldestLocked()
}
s.seen[hash] = &dedupEntry{
count: 1,
firstAt: now,
lastAt: now,
lastEmit: now,
}
return true
}
// Update stats
entry.count++
entry.lastAt = now
// Check if window has expired since last emit
if now-entry.lastEmit >= windowNano {
entry.lastEmit = now
return true
}
// Suppress this message
return false
}
// Sample implements the Sampler interface (always returns true for non-dedup use).
func (s *DedupSampler) Sample(lvl Level) bool {
return true
}
// GetSuppressedCount returns the count of suppressed messages for a key.
func (s *DedupSampler) GetSuppressedCount(lvl Level, msg string) int64 {
h := fnv.New64a()
h.Write([]byte{byte(lvl)})
h.Write([]byte(msg))
hash := h.Sum64()
s.mu.Lock()
defer s.mu.Unlock()
if entry, exists := s.seen[hash]; exists {
return entry.count - 1 // exclude the one we emitted
}
return 0
}
func (s *DedupSampler) cleanupLocked(now, windowNano int64) {
cutoff := now - windowNano
for hash, entry := range s.seen {
if entry.lastAt < cutoff {
delete(s.seen, hash)
}
}
}
func (s *DedupSampler) evictOldestLocked() {
var oldestHash uint64
var oldestTime int64 = 1<<63 - 1
for hash, entry := range s.seen {
if entry.lastAt < oldestTime {
oldestTime = entry.lastAt
oldestHash = hash
}
}
if oldestHash != 0 {
delete(s.seen, oldestHash)
}
}
+60 -27
View File
@@ -271,18 +271,20 @@ func LevelString(l slog.Level) string {
// GlogHandler is a log handler that mimics glog behavior.
type GlogHandler struct {
origin slog.Handler
verbosity slog.Level
vmodule map[string]slog.Level
mu sync.RWMutex
origin slog.Handler
verbosity slog.Level
vmodule map[string]slog.Level
patternCache map[string]*regexp.Regexp // cached compiled patterns
mu sync.RWMutex
}
// NewGlogHandler creates a GlogHandler wrapping the given handler.
func NewGlogHandler(h slog.Handler) *GlogHandler {
return &GlogHandler{
origin: h,
verbosity: slogLevelInfo,
vmodule: make(map[string]slog.Level),
origin: h,
verbosity: slogLevelInfo,
vmodule: make(map[string]slog.Level),
patternCache: make(map[string]*regexp.Regexp),
}
}
@@ -299,6 +301,7 @@ func (h *GlogHandler) Vmodule(pattern string) error {
defer h.mu.Unlock()
h.vmodule = make(map[string]slog.Level)
h.patternCache = make(map[string]*regexp.Regexp)
if pattern == "" {
return nil
}
@@ -320,6 +323,10 @@ func (h *GlogHandler) Vmodule(pattern string) error {
}
}
h.vmodule[module] = level
// Precompile pattern regex
if re, err := compilePattern(module); err == nil {
h.patternCache[module] = re
}
}
return nil
}
@@ -328,13 +335,15 @@ func (h *GlogHandler) Handle(ctx context.Context, r slog.Record) error {
h.mu.RLock()
verbosity := h.verbosity
vmodule := h.vmodule
patternCache := h.patternCache
h.mu.RUnlock()
if len(vmodule) > 0 {
_, file, _, ok := runtime.Caller(6)
if ok {
for pattern, level := range vmodule {
if matched, _ := matchPattern(pattern, file); matched && r.Level >= level {
re := patternCache[pattern]
if re != nil && re.MatchString(file) && r.Level >= level {
return h.origin.Handle(ctx, r)
}
}
@@ -347,15 +356,12 @@ func (h *GlogHandler) Handle(ctx context.Context, r slog.Record) error {
return nil
}
func matchPattern(pattern, file string) (bool, error) {
pattern = strings.ReplaceAll(pattern, ".", "\\.")
pattern = strings.ReplaceAll(pattern, "*", ".*")
pattern = strings.ReplaceAll(pattern, "/", "\\/")
re, err := regexp.Compile(pattern)
if err != nil {
return false, err
}
return re.MatchString(file), nil
// compilePattern converts a vmodule pattern to a compiled regex.
func compilePattern(pattern string) (*regexp.Regexp, error) {
escaped := strings.ReplaceAll(pattern, ".", "\\.")
escaped = strings.ReplaceAll(escaped, "*", ".*")
escaped = strings.ReplaceAll(escaped, "/", "\\/")
return regexp.Compile(escaped)
}
func (h *GlogHandler) Enabled(ctx context.Context, level slog.Level) bool {
@@ -372,12 +378,17 @@ func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
for k, v := range h.vmodule {
vmodule[k] = v
}
patternCache := make(map[string]*regexp.Regexp, len(h.patternCache))
for k, v := range h.patternCache {
patternCache[k] = v
}
h.mu.RUnlock()
return &GlogHandler{
origin: h.origin.WithAttrs(attrs),
verbosity: verbosity,
vmodule: vmodule,
origin: h.origin.WithAttrs(attrs),
verbosity: verbosity,
vmodule: vmodule,
patternCache: patternCache,
}
}
@@ -388,12 +399,17 @@ func (h *GlogHandler) WithGroup(name string) slog.Handler {
for k, v := range h.vmodule {
vmodule[k] = v
}
patternCache := make(map[string]*regexp.Regexp, len(h.patternCache))
for k, v := range h.patternCache {
patternCache[k] = v
}
h.mu.RUnlock()
return &GlogHandler{
origin: h.origin.WithGroup(name),
verbosity: verbosity,
vmodule: vmodule,
origin: h.origin.WithGroup(name),
verbosity: verbosity,
vmodule: vmodule,
patternCache: patternCache,
}
}
@@ -438,7 +454,24 @@ func (h *TerminalHandler) Enabled(_ context.Context, level slog.Level) bool {
}
func (h *TerminalHandler) WithGroup(name string) slog.Handler {
panic("not implemented")
// Create a new handler with the group prefix applied to attribute keys
return &TerminalHandler{
wr: h.wr,
lvl: h.lvl,
useColor: h.useColor,
attrs: h.attrs,
fieldPadding: make(map[string]int),
Prefix: func(r slog.Record) string {
prefix := ""
if h.Prefix != nil {
prefix = h.Prefix(r)
}
if name != "" {
prefix = prefix + name + "."
}
return prefix
},
}
}
func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
@@ -818,10 +851,10 @@ func DiscardHandler() slog.Handler {
type discardHandler struct{}
func (h *discardHandler) Handle(_ context.Context, r slog.Record) error { return nil }
func (h *discardHandler) Handle(_ context.Context, r slog.Record) error { return nil }
func (h *discardHandler) Enabled(_ context.Context, level slog.Level) bool { return false }
func (h *discardHandler) WithGroup(name string) slog.Handler { return h }
func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return h }
func (h *discardHandler) WithGroup(name string) slog.Handler { return h }
func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return h }
// TerminalStringer is an interface for custom terminal serialization.
type TerminalStringer interface {
+1 -1
View File
@@ -73,5 +73,5 @@ GOWORK=off go test ./...
* [`lattice/v7/ring`](https://github.com/luxfi/lattice/tree/main/ring)
— canonical Lattigo-derived NTT/Montgomery body that this module
delegates to.
* [`luxcpp/crypto/corona`](https://github.com/luxcpp/crypto/tree/main/corona)
* [`luxcpp/crypto/ringtail`](https://github.com/luxcpp/crypto/tree/main/ringtail)
— native C++ Montgomery NTT that Phase 6 mirrors as `math` C++.
+329
View File
@@ -0,0 +1,329 @@
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// ZAP-native metric exporter — the default metric transport.
//
// Metric families serialize to JSON inside a luxfi/zap envelope and ship
// over TCP to a ZAP-aware o11y collector (hanzo/o11y has a matching
// receiver). Zero google.golang.org/protobuf, zero OTLP, zero gRPC.
//
// Wire layout per export call:
//
// zap envelope, MsgType=MsgMetricBatch
// └─ root object
// └─ FieldPayload (bytes): JSON-encoded MetricBatch
//
// MetricBatch carries app/resource attributes + a list of MetricFamily
// rows (same shape as the in-process gatherer.Gather() output).
//
// Mirrors luxfi/trace/exporter_zap.go (MsgSpanBatch=1). The two
// transports share the same ZAP node + collector endpoint by convention
// (the o11y collector binds :4317 and routes by MsgType in the envelope
// flags field).
package metric
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"math"
"sync"
"time"
"github.com/luxfi/zap"
)
// MsgMetricBatch is the ZAP MsgType that carries a MetricBatch payload.
//
// Stable wire ID for the metric transport on the ZAP bus. Append-only —
// renumbering breaks every deployed collector in lockstep. Coordinates
// with luxfi/trace.MsgSpanBatch (=1); collectors switch on the
// MsgType-in-flags-upper-byte to route to the right ingest path.
const MsgMetricBatch uint16 = 2
// MetricBatch is the JSON shape that rides inside the ZAP envelope's
// payload field. One batch per Export call (typically one Gather()
// snapshot per scrape interval).
type MetricBatch struct {
AppName string `json:"appName,omitempty"`
Version string `json:"version,omitempty"`
Resource map[string]string `json:"resource,omitempty"`
TimestampNs int64 `json:"timestampNs"`
Families []MetricFamilyWire `json:"families"`
}
// MetricFamilyWire is the JSON-stable wire shape of a MetricFamily.
// Mirrors metric.MetricFamily but pins the field names so the collector
// side can decode without depending on this package's Go types.
type MetricFamilyWire struct {
Name string `json:"name"`
Help string `json:"help,omitempty"`
Type string `json:"type"`
Metrics []MetricWire `json:"metrics"`
}
// MetricWire is the JSON-stable wire shape of a single Metric.
type MetricWire struct {
Labels map[string]string `json:"labels,omitempty"`
Value *float64 `json:"value,omitempty"` // counter/gauge
SampleCount *uint64 `json:"sampleCount,omitempty"` // histogram/summary
SampleSum *float64 `json:"sampleSum,omitempty"` // histogram/summary
Buckets []BucketWire `json:"buckets,omitempty"` // histogram
Quantiles []QuantileWire `json:"quantiles,omitempty"` // summary
}
type BucketWire struct {
UpperBound float64 `json:"upperBound"`
CumulativeCount uint64 `json:"cumulativeCount"`
}
type QuantileWire struct {
Quantile float64 `json:"quantile"`
Value float64 `json:"value"`
}
// ZAPExporterConfig configures the ZAP-native exporter.
type ZAPExporterConfig struct {
// Endpoint is the collector address (host:port). Defaults to
// "127.0.0.1:4317" (the canonical o11y ZAP port).
Endpoint string
// AppName + Version land in every emitted batch's metadata.
AppName string
Version string
// Resource attributes shipped with every batch (e.g., k8s namespace,
// pod name, network ID).
Resource map[string]string
// Logger — defaults to slog.Default().
Logger *slog.Logger
}
// ZAPExporter ships metric families over a luxfi/zap connection.
//
// Fire-and-forget by design: tracing and metrics MUST NEVER block the
// host process. Send failures invalidate the cached connection and
// trigger a reconnect on the next Export call; no batches are buffered
// in memory.
type ZAPExporter struct {
cfg ZAPExporterConfig
mu sync.Mutex
node *zap.Node
serverID string
closed bool
}
// NewZAPExporter constructs a metric exporter that ships over ZAP.
//
// Returns the exporter even if the initial connect fails — the next
// Export call will retry. This matches the trace exporter's posture:
// fire-and-forget means startup never fails on collector reachability.
func NewZAPExporter(cfg ZAPExporterConfig) (*ZAPExporter, error) {
if cfg.Endpoint == "" {
cfg.Endpoint = "127.0.0.1:4317"
}
if cfg.Logger == nil {
cfg.Logger = slog.Default()
}
node := zap.NewNode(zap.NodeConfig{
NodeID: fmt.Sprintf("metric-%s", cfg.AppName),
ServiceType: "_o11y._tcp",
Port: 0,
Logger: cfg.Logger,
NoDiscovery: true,
})
if err := node.Start(); err != nil {
return nil, fmt.Errorf("metric zap exporter start: %w", err)
}
e := &ZAPExporter{
cfg: cfg,
node: node,
}
if err := e.connect(); err != nil {
// Log and continue — exporter retries on first export.
cfg.Logger.Debug("metric zap exporter: initial connect failed (will retry on export)",
"endpoint", cfg.Endpoint, "err", err)
}
return e, nil
}
// connect dials the collector and caches the peer ID.
//
// Idempotent under the mutex — called from NewZAPExporter and from
// Export when an earlier connect failed.
func (e *ZAPExporter) connect() error {
if err := e.node.ConnectDirect(e.cfg.Endpoint); err != nil {
return err
}
peers := e.node.Peers()
if len(peers) == 0 {
return fmt.Errorf("metric zap exporter: connected but no peer ID for %s", e.cfg.Endpoint)
}
e.serverID = peers[0]
return nil
}
// Export serializes families to JSON, wraps them in a ZAP envelope, and
// fires them over the cached connection. Fire-and-forget — no response
// is expected, no waiting on the collector.
func (e *ZAPExporter) Export(ctx context.Context, families []*MetricFamily) error {
if len(families) == 0 {
return nil
}
e.mu.Lock()
defer e.mu.Unlock()
if e.closed {
return fmt.Errorf("metric zap exporter: shut down")
}
if e.serverID == "" {
if err := e.connect(); err != nil {
e.cfg.Logger.Debug("metric zap exporter: connect retry failed; dropping batch",
"endpoint", e.cfg.Endpoint, "err", err)
return nil
}
}
batch := MetricBatch{
AppName: e.cfg.AppName,
Version: e.cfg.Version,
Resource: e.cfg.Resource,
TimestampNs: time.Now().UnixNano(),
Families: make([]MetricFamilyWire, 0, len(families)),
}
for _, fam := range families {
batch.Families = append(batch.Families, translateFamily(fam))
}
payload, err := json.Marshal(&batch)
if err != nil {
return fmt.Errorf("metric zap exporter: marshal batch: %w", err)
}
wire, err := encodeMetricBatch(payload)
if err != nil {
return err
}
msg, err := zap.Parse(wire)
if err != nil {
return fmt.Errorf("metric zap exporter: parse outgoing: %w", err)
}
if err := e.node.Send(ctx, e.serverID, msg); err != nil {
// Connection died — invalidate and let the next call reconnect.
e.serverID = ""
e.cfg.Logger.Debug("metric zap exporter: send failed; will reconnect", "err", err)
return nil
}
return nil
}
// ExportGatherer is the convenience entry point — calls Gather() on the
// supplied Gatherer and ships the resulting families. Use this when
// driving the exporter from a scrape loop.
func (e *ZAPExporter) ExportGatherer(ctx context.Context, g Gatherer) error {
families, err := g.Gather()
if err != nil {
return fmt.Errorf("metric zap exporter: gather: %w", err)
}
return e.Export(ctx, families)
}
// Shutdown closes the ZAP node. Safe to call multiple times.
func (e *ZAPExporter) Shutdown(_ context.Context) error {
e.mu.Lock()
defer e.mu.Unlock()
if e.closed {
return nil
}
e.closed = true
e.node.Stop()
return nil
}
// translateFamily converts an in-process MetricFamily into the JSON
// wire shape. Maps MetricType → string label, label slices → maps, and
// only emits the value fields that match the metric type.
func translateFamily(fam *MetricFamily) MetricFamilyWire {
out := MetricFamilyWire{
Name: fam.Name,
Help: fam.Help,
Type: fam.Type.String(),
Metrics: make([]MetricWire, 0, len(fam.Metrics)),
}
for _, m := range fam.Metrics {
mw := MetricWire{}
if len(m.Labels) > 0 {
mw.Labels = make(map[string]string, len(m.Labels))
for _, lp := range m.Labels {
mw.Labels[lp.Name] = lp.Value
}
}
switch fam.Type {
case MetricTypeCounter, MetricTypeGauge:
v := m.Value.Value
mw.Value = &v
case MetricTypeHistogram:
c := m.Value.SampleCount
s := m.Value.SampleSum
mw.SampleCount = &c
mw.SampleSum = &s
if len(m.Value.Buckets) > 0 {
// Drop the trailing +Inf bucket. JSON can't represent
// +Inf as a number and the +Inf bucket count is always
// equal to SampleCount — it's already on the wire.
mw.Buckets = make([]BucketWire, 0, len(m.Value.Buckets))
for _, b := range m.Value.Buckets {
if math.IsInf(b.UpperBound, +1) {
continue
}
mw.Buckets = append(mw.Buckets, BucketWire{
UpperBound: b.UpperBound,
CumulativeCount: b.CumulativeCount,
})
}
}
case MetricTypeSummary:
c := m.Value.SampleCount
s := m.Value.SampleSum
mw.SampleCount = &c
mw.SampleSum = &s
if len(m.Value.Quantiles) > 0 {
mw.Quantiles = make([]QuantileWire, len(m.Value.Quantiles))
for i, q := range m.Value.Quantiles {
mw.Quantiles[i] = QuantileWire{
Quantile: q.Quantile,
Value: q.Value,
}
}
}
}
out.Metrics = append(out.Metrics, mw)
}
return out
}
// encodeMetricBatch wraps a JSON payload in a ZAP envelope tagged
// MsgMetricBatch.
//
// Wire shape:
//
// zap header (16) + root object { bytes payload @ offset 0 }
//
// MsgMetricBatch goes in the upper 8 bits of the ZAP flags field —
// same convention luxfi/trace uses for MsgSpanBatch.
func encodeMetricBatch(payload []byte) ([]byte, error) {
const envelopeSize = 16
b := zap.NewBuilder(envelopeSize + 64 + len(payload))
root := b.StartObject(envelopeSize)
root.SetBytes(0, payload)
root.FinishAsRoot()
return b.FinishWithFlags(MsgMetricBatch << 8), nil
}
+6 -6
View File
@@ -254,7 +254,7 @@
</div>
<div class="legend-item">
<div class="legend-color" style="background: #f093fb;"></div>
<span>Corona</span>
<span>Ringtail</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #4facfe;"></div>
@@ -340,7 +340,7 @@
<td>Level 5</td>
</tr>
<tr>
<td><strong>Corona (Hybrid)</strong></td>
<td><strong>Ringtail (Hybrid)</strong></td>
<td class="good">6,780</td>
<td class="good">0.15 ms</td>
<td class="good">99.97%</td>
@@ -435,7 +435,7 @@
tension: 0.4
},
{
label: 'Corona',
label: 'Ringtail',
data: Array(20).fill(0).map(() => 6500 + Math.random() * 500),
borderColor: '#f093fb',
tension: 0.4
@@ -456,7 +456,7 @@
const verificationChart = new Chart(verificationCtx, {
type: 'bar',
data: {
labels: ['BLS', 'ML-DSA', 'SLH-DSA', 'Corona', 'Verkle'],
labels: ['BLS', 'ML-DSA', 'SLH-DSA', 'Ringtail', 'Verkle'],
datasets: [{
label: 'Average Verification Time',
data: [0.08, 0.12, 0.47, 0.15, 0.05],
@@ -488,7 +488,7 @@
const gasChart = new Chart(gasCtx, {
type: 'doughnut',
data: {
labels: ['BLS', 'ML-DSA', 'SLH-DSA', 'Corona', 'Verkle', 'Other'],
labels: ['BLS', 'ML-DSA', 'SLH-DSA', 'Ringtail', 'Verkle', 'Other'],
datasets: [{
data: [25, 30, 10, 20, 10, 5],
backgroundColor: [
@@ -556,7 +556,7 @@
dataset.data.shift();
const base = dataset.label === 'BLS' ? 12000 :
dataset.label === 'ML-DSA' ? 8000 :
dataset.label === 'Corona' ? 6500 : 18000;
dataset.label === 'Ringtail' ? 6500 : 18000;
dataset.data.push(base + Math.random() * 1000);
});
signatureChart.update('none');
+1 -1
View File
@@ -57,7 +57,7 @@ import (
// TLS handshake completes. PubKey + Sig are opaque bytes from
// zap's perspective; the AttestationVerifier owns the format
// (FIPS 204 ML-DSA-65 pubkey 1952 bytes, signature 3293 bytes
// for this network; ML-DSA-87 with different byte counts for
// for Liquid; ML-DSA-87 with different byte counts for
// high-value Zoo chains).
type Attestation struct {
// PubKey is the peer's strict-PQ public key. The verifier
+19 -7
View File
@@ -5,13 +5,13 @@ package colorable
import (
"bytes"
syscall "golang.org/x/sys/windows"
"io"
"math"
"os"
"strconv"
"strings"
"sync"
syscall "golang.org/x/sys/windows"
"unsafe"
"github.com/mattn/go-isatty"
@@ -93,6 +93,7 @@ type writer struct {
handle syscall.Handle
althandle syscall.Handle
oldattr word
curattr word
oldpos coord
rest bytes.Buffer
mutex sync.Mutex
@@ -112,7 +113,7 @@ func NewColorable(file *os.File) io.Writer {
var csbi consoleScreenBufferInfo
handle := syscall.Handle(file.Fd())
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
return &writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
return &writer{out: file, handle: handle, oldattr: csbi.attributes, curattr: csbi.attributes, oldpos: coord{0, 0}}
}
return file
}
@@ -438,7 +439,11 @@ func (w *writer) Write(data []byte) (n int, err error) {
w.mutex.Lock()
defer w.mutex.Unlock()
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
if w.rest.Len() == 0 && bytes.IndexByte(data, 0x1b) == -1 {
w.out.Write(data)
return len(data), nil
}
handle := w.handle
@@ -517,7 +522,7 @@ loop:
w.rest.Reset()
break
}
buf.Write([]byte(string(c)))
buf.WriteByte(c)
}
if m == 0 {
break loop
@@ -678,11 +683,11 @@ loop:
procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
case 'm':
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
attr := csbi.attributes
attr := w.curattr
cs := buf.String()
if cs == "" {
procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr))
w.curattr = w.oldattr
continue
}
token := strings.Split(cs, ";")
@@ -814,9 +819,12 @@ loop:
attr |= backgroundBlue
}
}
procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr))
}
}
if attr != w.curattr {
procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr))
w.curattr = attr
}
case 'h':
var ci consoleCursorInfo
cs := buf.String()
@@ -834,6 +842,8 @@ loop:
w.althandle = syscall.Handle(h)
if w.althandle != 0 {
handle = w.althandle
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
w.curattr = csbi.attributes
}
}
}
@@ -853,6 +863,8 @@ loop:
syscall.CloseHandle(w.althandle)
w.althandle = 0
handle = w.handle
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
w.curattr = csbi.attributes
}
}
case 's':
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build (appengine || js || nacl || tinygo || wasm) && !windows
// +build appengine js nacl tinygo wasm
//go:build (appengine || js || nacl || tinygo || wasm || wasip1 || wasip2) && !windows
// +build appengine js nacl tinygo wasm wasip1 wasip2
// +build !windows
package isatty
+13 -2
View File
@@ -31,6 +31,10 @@ func init() {
if procGetFileInformationByHandleEx.Find() != nil {
procGetFileInformationByHandleEx = nil
}
// Check if NtQueryObject is available.
if procNtQueryObject.Find() != nil {
procNtQueryObject = nil
}
}
// IsTerminal return true if the file descriptor is terminal.
@@ -43,6 +47,7 @@ func IsTerminal(fd uintptr) bool {
// Check pipe name is used for cygwin/msys2 pty.
// Cygwin/MSYS2 PTY has a name like:
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
// On Windows 7 a trailing suffix (e.g. "-nat") may be appended.
func isCygwinPipeName(name string) bool {
token := strings.Split(name, "-")
if len(token) < 5 {
@@ -72,13 +77,19 @@ func isCygwinPipeName(name string) bool {
return false
}
for _, t := range token[5:] {
if t == "" {
return false
}
}
return true
}
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
// getFileNameByHandle use the undocumented ntdll NtQueryObject to get file full name from file handler
// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
// Windows vista to 10
// Windows Vista to 10
// see https://stackoverflow.com/a/18792477 for details
func getFileNameByHandle(fd uintptr) (string, error) {
if procNtQueryObject == nil {
+1
View File
@@ -3,6 +3,7 @@ MIT License
Copyright (c) 2017 Denis Subbotin
Copyright (c) 2017 Nika Jones
Copyright (c) 2017 Philip Schlump
Copyright (c) 2020 Peter 'ribasushi' Rabbitson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+5 -1
View File
@@ -1,6 +1,7 @@
package base58
// Alphabet is a a b58 alphabet.
// Alphabet is a a b58 alphabet.\
// Deprecated: use github.com/mr-tron/base58.Alphabet instead.
type Alphabet struct {
decode [128]int8
encode [58]byte
@@ -9,6 +10,7 @@ type Alphabet struct {
// NewAlphabet creates a new alphabet from the passed string.
//
// It panics if the passed string is not 58 bytes long or isn't valid ASCII.
// Deprecated: use github.com/mr-tron/base58.NewAlphabet instead.
func NewAlphabet(s string) *Alphabet {
if len(s) != 58 {
panic("base58 alphabets must be 58 bytes long")
@@ -25,7 +27,9 @@ func NewAlphabet(s string) *Alphabet {
}
// BTCAlphabet is the bitcoin base58 alphabet.
// Deprecated: use github.com/mr-tron/base58.BTCAlphabet instead.
var BTCAlphabet = NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
// FlickrAlphabet is the flickr base58 alphabet.
// Deprecated: use github.com/mr-tron/base58.FlickrAlphabet instead.
var FlickrAlphabet = NewAlphabet("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ")
+12
View File
@@ -11,23 +11,27 @@ var (
)
// Encode encodes the passed bytes into a base58 encoded string.
// Deprecated: use github.com/mr-tron/base58.Encode instead.
func Encode(bin []byte) string {
return FastBase58Encoding(bin)
}
// EncodeAlphabet encodes the passed bytes into a base58 encoded string with the
// passed alphabet.
// Deprecated: use github.com/mr-tron/base58.EncodeAlphabet instead.
func EncodeAlphabet(bin []byte, alphabet *Alphabet) string {
return FastBase58EncodingAlphabet(bin, alphabet)
}
// FastBase58Encoding encodes the passed bytes into a base58 encoded string.
// Deprecated: use github.com/mr-tron/base58.FastBase58Encoding instead.
func FastBase58Encoding(bin []byte) string {
return FastBase58EncodingAlphabet(bin, BTCAlphabet)
}
// FastBase58EncodingAlphabet encodes the passed bytes into a base58 encoded
// string with the passed alphabet.
// Deprecated: use github.com/mr-tron/base58.FastBase58EncodingAlphabet instead.
func FastBase58EncodingAlphabet(bin []byte, alphabet *Alphabet) string {
zero := alphabet.encode[0]
@@ -80,12 +84,14 @@ func FastBase58EncodingAlphabet(bin []byte, alphabet *Alphabet) string {
// TrivialBase58Encoding encodes the passed bytes into a base58 encoded string
// (inefficiently).
// Deprecated: use github.com/mr-tron/base58.FastBase58Encoding instead.
func TrivialBase58Encoding(a []byte) string {
return TrivialBase58EncodingAlphabet(a, BTCAlphabet)
}
// TrivialBase58EncodingAlphabet encodes the passed bytes into a base58 encoded
// string (inefficiently) with the passed alphabet.
// Deprecated: use github.com/mr-tron/base58.FastBase58EncodingAlphabet instead.
func TrivialBase58EncodingAlphabet(a []byte, alphabet *Alphabet) string {
zero := alphabet.encode[0]
idx := len(a)*138/100 + 1
@@ -108,22 +114,26 @@ func TrivialBase58EncodingAlphabet(a []byte, alphabet *Alphabet) string {
}
// Decode decodes the base58 encoded bytes.
// Deprecated: use github.com/mr-tron/base58.Decode instead.
func Decode(str string) ([]byte, error) {
return FastBase58Decoding(str)
}
// DecodeAlphabet decodes the base58 encoded bytes using the given b58 alphabet.
// Deprecated: use github.com/mr-tron/base58.DecodeAlphabet instead.
func DecodeAlphabet(str string, alphabet *Alphabet) ([]byte, error) {
return FastBase58DecodingAlphabet(str, alphabet)
}
// FastBase58Decoding decodes the base58 encoded bytes.
// Deprecated: use github.com/mr-tron/base58.Decode instead.
func FastBase58Decoding(str string) ([]byte, error) {
return FastBase58DecodingAlphabet(str, BTCAlphabet)
}
// FastBase58DecodingAlphabet decodes the base58 encoded bytes using the given
// b58 alphabet.
// Deprecated: use github.com/mr-tron/base58.FastBase58DecodingAlphabet instead.
func FastBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) {
if len(str) == 0 {
return nil, fmt.Errorf("zero length string")
@@ -227,12 +237,14 @@ func FastBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error)
}
// TrivialBase58Decoding decodes the base58 encoded bytes (inefficiently).
// Deprecated: use github.com/mr-tron/base58.Decode instead.
func TrivialBase58Decoding(str string) ([]byte, error) {
return TrivialBase58DecodingAlphabet(str, BTCAlphabet)
}
// TrivialBase58DecodingAlphabet decodes the base58 encoded bytes
// (inefficiently) using the given b58 alphabet.
// Deprecated: use github.com/mr-tron/base58.FastBase58DecodingAlphabet instead.
func TrivialBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) {
zero := alphabet.encode[0]
+31 -19
View File
@@ -4,6 +4,8 @@ filippo.io/hpke
filippo.io/hpke/crypto
filippo.io/hpke/crypto/ecdh
filippo.io/hpke/internal/byteorder
# github.com/bits-and-blooms/bitset v1.24.4
## explicit; go 1.16
# github.com/btcsuite/btcd/btcutil v1.1.6
## explicit; go 1.16
github.com/btcsuite/btcd/btcutil/bech32
@@ -45,6 +47,10 @@ github.com/cloudflare/circl/sign/mldsa/mldsa87/internal
github.com/cloudflare/circl/simd/keccakf1600
github.com/cloudflare/circl/xof
github.com/cloudflare/circl/xof/k12
# github.com/consensys/gnark-crypto v0.20.1
## explicit; go 1.25.7
# github.com/crate-crypto/go-eth-kzg v1.5.0
## explicit; go 1.22
# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
## explicit
github.com/davecgh/go-spew/spew
@@ -64,6 +70,8 @@ github.com/dustin/go-humanize
## explicit; go 1.13
github.com/emicklei/go-restful/v3
github.com/emicklei/go-restful/v3/log
# github.com/ethereum/c-kzg-4844/v2 v2.1.7
## explicit; go 1.19
# github.com/fxamacker/cbor/v2 v2.9.1
## explicit; go 1.20
github.com/fxamacker/cbor/v2
@@ -125,7 +133,7 @@ github.com/josharian/intern
# github.com/json-iterator/go v1.1.12
## explicit; go 1.12
github.com/json-iterator/go
# github.com/klauspost/compress v1.18.5
# github.com/klauspost/compress v1.18.6
## explicit; go 1.24
github.com/klauspost/compress
github.com/klauspost/compress/fse
@@ -144,8 +152,8 @@ github.com/klauspost/cpuid/v2
# github.com/klauspost/crc32 v1.3.0
## explicit; go 1.23.0
github.com/klauspost/crc32
# github.com/luxfi/accel v1.1.9
## explicit; go 1.26.3
# github.com/luxfi/accel v1.2.4
## explicit; go 1.26.4
github.com/luxfi/accel
github.com/luxfi/accel/internal/capi
# github.com/luxfi/address v1.0.1
@@ -167,8 +175,8 @@ github.com/luxfi/constants
# github.com/luxfi/container v0.0.4
## explicit; go 1.25.5
github.com/luxfi/container
# github.com/luxfi/crypto v1.19.17
## explicit; go 1.26.3
# github.com/luxfi/crypto v1.19.27
## explicit; go 1.26.4
github.com/luxfi/crypto
github.com/luxfi/crypto/backend
github.com/luxfi/crypto/bls
@@ -185,11 +193,13 @@ github.com/luxfi/crypto/secp256k1/libsecp256k1/include
github.com/luxfi/crypto/secp256k1/libsecp256k1/src
github.com/luxfi/crypto/secp256k1/libsecp256k1/src/modules/recovery
github.com/luxfi/crypto/secret
# github.com/luxfi/crypto/ipa v1.2.4
## explicit; go 1.18
# github.com/luxfi/formatting v1.0.1
## explicit; go 1.25.5
github.com/luxfi/formatting
# github.com/luxfi/geth v1.16.98
## explicit; go 1.26.3
# github.com/luxfi/geth v1.17.12
## explicit; go 1.26.4
github.com/luxfi/geth/common
github.com/luxfi/geth/common/hexutil
# github.com/luxfi/go-bip32 v1.0.2
@@ -205,8 +215,8 @@ github.com/luxfi/ids
# github.com/luxfi/keys v1.1.0
## explicit; go 1.26.3
github.com/luxfi/keys
# github.com/luxfi/log v1.4.1
## explicit; go 1.24.9
# github.com/luxfi/log v1.4.3
## explicit; go 1.26.3
github.com/luxfi/log
github.com/luxfi/log/internal/json
# github.com/luxfi/math v1.4.1
@@ -221,14 +231,16 @@ github.com/luxfi/math/big
# github.com/luxfi/mdns v0.1.1
## explicit; go 1.26.3
github.com/luxfi/mdns
# github.com/luxfi/metric v1.5.7
# github.com/luxfi/metric v1.5.8
## explicit; go 1.26.3
github.com/luxfi/metric
github.com/luxfi/metric/client
# github.com/luxfi/mock v0.1.1
## explicit; go 1.25.5
github.com/luxfi/mock/gomock
# github.com/luxfi/proto v1.0.0
# github.com/luxfi/pq v1.0.3
## explicit; go 1.23.0
# github.com/luxfi/proto v1.3.4
## explicit; go 1.26.3
github.com/luxfi/proto/p/signer
# github.com/luxfi/sampler v1.1.0
@@ -237,8 +249,8 @@ github.com/luxfi/sampler
# github.com/luxfi/tls v1.0.3
## explicit; go 1.25.5
github.com/luxfi/tls
# github.com/luxfi/vm v1.2.0
## explicit; go 1.26.3
# github.com/luxfi/vm v1.2.3
## explicit; go 1.26.4
github.com/luxfi/vm/components/verify
# github.com/luxfi/zap v0.7.2
## explicit; go 1.26.3
@@ -259,11 +271,11 @@ github.com/luxfi/zapdb/y
github.com/mailru/easyjson/buffer
github.com/mailru/easyjson/jlexer
github.com/mailru/easyjson/jwriter
# github.com/mattn/go-colorable v0.1.14
# github.com/mattn/go-colorable v0.1.15
## explicit; go 1.18
github.com/mattn/go-colorable
# github.com/mattn/go-isatty v0.0.20
## explicit; go 1.15
# github.com/mattn/go-isatty v0.0.22
## explicit; go 1.21
github.com/mattn/go-isatty
# github.com/miekg/dns v1.1.72
## explicit; go 1.24.0
@@ -296,8 +308,8 @@ github.com/modern-go/concurrent
# github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee
## explicit; go 1.12
github.com/modern-go/reflect2
# github.com/mr-tron/base58 v1.2.0
## explicit; go 1.12
# github.com/mr-tron/base58 v1.3.0
## explicit; go 1.11
github.com/mr-tron/base58/base58
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
## explicit
@@ -384,7 +396,7 @@ golang.org/x/crypto/pbkdf2
golang.org/x/crypto/ripemd160
golang.org/x/crypto/scrypt
golang.org/x/crypto/sha3
# golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90
# golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9
## explicit; go 1.25.0
golang.org/x/exp/constraints
# golang.org/x/mod v0.36.0