Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/IBM/idemix v0.0.2-0.20250313153527-832db18b9478
github.com/IBM/idemix/bccsp/types v0.0.0-20250313153527-832db18b9478
github.com/IBM/mathlib v0.0.3-0.20251201181318-11a3ec7f764f
github.com/IBM/mathlib v0.0.3-0.20251209063833-bdbf97a6c3d2
github.com/dgraph-io/ristretto/v2 v2.3.0
github.com/gin-gonic/gin v1.10.0
github.com/hashicorp/go-uuid v1.0.3
Expand All @@ -27,7 +27,7 @@ require (
github.com/sourcegraph/conc v0.3.0
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.11.1
github.com/tedsuo/ifrit v0.0.0-20230516164442-7862c310ad26
github.com/test-go/testify v1.1.4
github.com/thedevsaddam/gojsonq v2.3.0+incompatible
Expand Down Expand Up @@ -76,7 +76,7 @@ require (
github.com/cockroachdb/errors v1.12.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/consensys/gnark-crypto v0.18.1 // indirect
github.com/consensys/gnark-crypto v0.19.2 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,8 @@ github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20250313153527-832db18b9478 h
github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20250313153527-832db18b9478/go.mod h1:k4Q5EYKRnYC6t80ipSCY3G8H4FdcxRa8jjlsJdGfNCY=
github.com/IBM/idemix/bccsp/types v0.0.0-20250313153527-832db18b9478 h1:Uzmcb4pNb54/fbAjnrZTiJwWV74+twP60N4qBGm4PvU=
github.com/IBM/idemix/bccsp/types v0.0.0-20250313153527-832db18b9478/go.mod h1:Pi1QIuIZ+1OXIbnYe27vNwJOnSq2WvkHRT/sfweTw8E=
github.com/IBM/mathlib v0.0.3-0.20251201181318-11a3ec7f764f h1:UyHWQt3a/XrM8u/x6KukEzyTABzfeVLZJn40hIIclsM=
github.com/IBM/mathlib v0.0.3-0.20251201181318-11a3ec7f764f/go.mod h1:O230ebw6/22B7T4C03b99ZcPtc5XAfBTOp+ZT+xmMCk=
github.com/IBM/mathlib v0.0.3-0.20251209063833-bdbf97a6c3d2 h1:xxqXQL645JpGvuUqWdNUHCY/6EwxqsmuBuiEUsbswQU=
github.com/IBM/mathlib v0.0.3-0.20251209063833-bdbf97a6c3d2/go.mod h1:rq67W1H6L1eorrE7DZ/HcSY/pfMDjbPWOx12SeUfQDk=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
Expand Down Expand Up @@ -736,8 +736,8 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI=
github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80=
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
Expand Down Expand Up @@ -1532,8 +1532,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/sykesm/zap-logfmt v0.0.4 h1:U2WzRvmIWG1wDLCFY3sz8UeEmsdHQjHFNlIdmroVFaI=
Expand Down
33 changes: 18 additions & 15 deletions token/core/common/crypto/math/curves.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

math "github.com/IBM/mathlib"
"github.com/IBM/mathlib/driver"
"github.com/IBM/mathlib/driver/gurvy"
"github.com/IBM/mathlib/driver/gurvy/bls12381"
"github.com/hyperledger-labs/fabric-token-sdk/token/core/common/crypto/rng"
)

Expand All @@ -22,20 +22,23 @@ var (

func init() {
BLS12_381_BBS_GURVY_FAST_RNG = math.CurveID(len(math.Curves))
math.Curves = append(math.Curves, math.NewCurve(
NewCurveWithFastRNG(gurvy.NewBls12_381BBS()),
math.NewG1(gurvy.NewBls12_381BBS().GenG1(), BLS12_381_BBS_GURVY_FAST_RNG),
math.NewG2(gurvy.NewBls12_381BBS().GenG2(), BLS12_381_BBS_GURVY_FAST_RNG),
math.NewGt(gurvy.NewBls12_381BBS().GenGt(), BLS12_381_BBS_GURVY_FAST_RNG),
math.NewZr(gurvy.NewBls12_381().GroupOrder(), BLS12_381_BBS_GURVY_FAST_RNG),
gurvy.NewBls12_381BBS().CoordinateByteSize(),
gurvy.NewBls12_381BBS().G1ByteSize(),
gurvy.NewBls12_381BBS().CompressedG1ByteSize(),
gurvy.NewBls12_381BBS().G2ByteSize(),
gurvy.NewBls12_381BBS().CompressedG2ByteSize(),
gurvy.NewBls12_381BBS().ScalarByteSize(),
BLS12_381_BBS_GURVY_FAST_RNG,
))
math.Curves = append(
math.Curves,
math.NewCurve(
NewCurveWithFastRNG(bls12381.NewBls12_381BBS()),
math.NewG1(bls12381.NewBls12_381BBS().GenG1(), BLS12_381_BBS_GURVY_FAST_RNG),
math.NewG2(bls12381.NewBls12_381BBS().GenG2(), BLS12_381_BBS_GURVY_FAST_RNG),
math.NewGt(bls12381.NewBls12_381BBS().GenGt(), BLS12_381_BBS_GURVY_FAST_RNG),
math.NewZr(bls12381.NewBls12_381().GroupOrder(), BLS12_381_BBS_GURVY_FAST_RNG),
bls12381.NewBls12_381BBS().CoordinateByteSize(),
bls12381.NewBls12_381BBS().G1ByteSize(),
bls12381.NewBls12_381BBS().CompressedG1ByteSize(),
bls12381.NewBls12_381BBS().G2ByteSize(),
bls12381.NewBls12_381BBS().CompressedG2ByteSize(),
bls12381.NewBls12_381BBS().ScalarByteSize(),
BLS12_381_BBS_GURVY_FAST_RNG,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that something that we need to tune during testing? or does this fix? (does it worth to have it in config file)?

),
)
}

type CurveWithFastRNG struct {
Expand Down
41 changes: 41 additions & 0 deletions token/core/common/crypto/slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package crypto

import "encoding/binary"

// AppendFixed32 appends slices prefixed with a 4-byte Little Endian length.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little Endian this means that we support only x86 not power/z deployments?

// Format: [Len(4 bytes)][Data]...
func AppendFixed32(dst []byte, s [][]byte) []byte {
// 1. Precise Size Calculation
// We calculate the exact total growth needed (4 bytes header + data length per slice).
// This allows us to perform exactly one allocation.
const headerSize = 4
n := 0
for _, v := range s {
n += headerSize + len(v)
}

// 2. Single Growth / Allocation
// If the capacity is insufficient, we grow the slice exactly once.
// This avoids the 2x growth strategy of standard append(), saving
// potentially 25-50% memory overhead on large buffers.
if cap(dst)-len(dst) < n {
newDst := make([]byte, len(dst), len(dst)+n)
copy(newDst, dst)
dst = newDst
}

// 3. Append Loop (Branch-free)
for _, v := range s {
// AppendUint32 is inlinable and highly optimized in Go 1.19+ [web:22]
dst = binary.LittleEndian.AppendUint32(dst, uint32(len(v)))
dst = append(dst, v...)
}

return dst
}
120 changes: 120 additions & 0 deletions token/core/common/crypto/slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package crypto

import (
"bytes"
"encoding/binary"
"testing"

"github.com/stretchr/testify/assert"
)

// Unit Test: Verifies correct Little Endian encoding and data integrity
func TestAppendFixed32(t *testing.T) {
tests := []struct {
name string
input [][]byte
expected []byte
}{
{
name: "Basic Join",
input: [][]byte{
[]byte("Go"),
[]byte("Lang"),
},
// Expect: [Len:2][G][o] [Len:4][L][a][n][g]
// Little Endian 2: 0x02, 0x00, 0x00, 0x00
expected: []byte{
0x02, 0x00, 0x00, 0x00, 'G', 'o',
0x04, 0x00, 0x00, 0x00, 'L', 'a', 'n', 'g',
},
},
{
name: "Empty Input",
input: [][]byte{},
expected: nil, // Or empty slice depending on init
},
{
name: "Contains Empty Slice",
input: [][]byte{
[]byte("Hi"),
{},
},
// Expect: [Len:2][H][i] [Len:0]
expected: []byte{
0x02, 0x00, 0x00, 0x00, 'H', 'i',
0x00, 0x00, 0x00, 0x00,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Using nil as dst to force new allocation
result := AppendFixed32(nil, tt.input)
assert.Equal(t, tt.expected, result)
})
}
}

// Verification Test: Reusing an existing buffer
func TestAppendFixed32_ReuseBuffer(t *testing.T) {
buffer := make([]byte, 0, 1024)
buffer = append(buffer, 0xFF) // Simulating existing data (dirty buffer)

input := [][]byte{[]byte("A")}
result := AppendFixed32(buffer, input)

// Check that we didn't lose the prefix 0xFF
assert.Equal(t, byte(0xFF), result[0])
// Check the new data starts at index 1
// Len: 1 (0x01 00 00 00) + 'A'
expectedPayload := []byte{0x01, 0x00, 0x00, 0x00, 'A'}
assert.Equal(t, expectedPayload, result[1:])
}

// --- Benchmarks ---

// Setup helper for benchmarks
func generateBenchmarkData(count, size int) [][]byte {
data := make([][]byte, count)
for i := 0; i < count; i++ {
data[i] = bytes.Repeat([]byte{'a'}, size)
}
return data
}

// Optimized Approach
func BenchmarkAppendFixed32(b *testing.B) {
// Scenario: 1000 items, 256 bytes each (~250KB total)
data := generateBenchmarkData(1000, 256)

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
// Use nil to strictly measure allocation of the result
_ = AppendFixed32(nil, data)
}
}

// Comparison: Naive Loop (No pre-calculation)
func BenchmarkAppendFixed32_Naive(b *testing.B) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add doc to this one?
It is not clear to me what this benchmarks tests and how to connect it to the Approver performance?

data := generateBenchmarkData(1000, 256)

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
var dst []byte
for _, v := range data {
dst = binary.LittleEndian.AppendUint32(dst, uint32(len(v)))
dst = append(dst, v...)
}
}
}
31 changes: 25 additions & 6 deletions token/core/zkatdlog/nogh/v1/crypto/common/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ SPDX-License-Identifier: Apache-2.0
package common

import (
"bytes"
"encoding/hex"
"hash"

math "github.com/IBM/mathlib"
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
"github.com/hyperledger-labs/fabric-token-sdk/token/core/common/crypto"
)

// Separator is used to delimit to end an array of bytes.
Expand All @@ -28,11 +28,21 @@ func (a *G1Array) Bytes() ([]byte, error) {
if e == nil {
return nil, errors.Errorf("failed to marshal array of G1")
}
st := hex.EncodeToString(e.Bytes())
raw[i] = []byte(st)
raw[i] = e.Bytes()
}
// join the serialization of the group elements with the predefined separator.
return bytes.Join(raw, []byte(Separator)), nil
return crypto.AppendFixed32([]byte{}, raw), nil
}

func (a *G1Array) BytesTo(b []byte) ([]byte, error) {
raw := make([][]byte, len([]*math.G1(*a)))
for i, e := range []*math.G1(*a) {
if e == nil {
return nil, errors.Errorf("failed to marshal array of G1")
}
raw[i] = e.Bytes()
}
clear(b)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the clear do? Is that ok to clear the "b" here? what happen if the caller needs it? Maybe to add flag to clear and this will be clear to the caller.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if "b" is nil?

return crypto.AppendFixed32(b, raw), nil
}

// GetG1Array takes a series of G1 elements and returns the corresponding array
Expand All @@ -44,3 +54,12 @@ func GetG1Array(elements ...[]*math.G1) *G1Array {
a := G1Array(array)
return &a
}

func HashG1Array(h hash.Hash, elements ...*math.G1) []byte {
h.Reset()

for _, e := range elements {
h.Write(e.Bytes())
}
return h.Sum(nil)
}
Loading
Loading