bulletproofs

package module
v0.0.0-...-1dd1c8f Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2025 License: MIT Imports: 9 Imported by: 0

README

Bulletproofs++ implementation

⚠️ Please note - this crypto library has not been audited, so use it at your own risk. ⚠️ Please note - this library has been forked from the original to remove depndencies on go-ethereum.

Abstract

Present Go library contains the implementation of Bulletproofs++ weight norm linear argument protocol, arithmetic circuit protocol and reciprocal range proof protocol described in Distributed Lab's Bulletproofs++ Construction and Examples.

Explore the circuit_test.go to check the examples of circuit prove and verification. It contains several circuits:

  • Prove that we know such x, y that x+y=r and x*y=z for public r, z. This example encoded directly into the BP++ circuits.
  • Prove the range for 4-bits value: x is in [0..2^n) range (simple binary range proof).

Also, reciprocal_test.go contains example of proving that value lies in [0, 16^n) range.

Implemented protocol has 2G points advantage over existing BP and BP+ protocols on proving of one 64-bit value and this advantage will increase for more values per proof.

Protocol G F
BP 16 5
BP+ 15 3
Our BP++ 13 3

Reciprocal range proofs

Check the following snippet as an example of usage of range proof protocol:

package main

import (
	"github.com/cloudflare/bn256"
	"github.com/afsheenb/bulletproofs"
	"math/big"
)

func main() {
	// The uint64 in 16-base system will be encoded in 16 digits. 
	// The 16 base is selected as the most optimal base for this case.

	// Our private value is 0xab4f0540ab4f0540. 
	x := uint64(0xab4f0540ab4f0540)
	X := new(big.Int).SetUint64(x)

	// Let's encode it as a list of digits:
	digits := bulletproofs.UInt64Hex(x) // [0 4 5 0 15 4 11 10 0 4 5 0 15 4 11 10]

	// Public poles multiplicities i-th element corresponds to the 'i-digit' multiplicity (the count of 'i-digit' in digits list)
	m := bulletproofs.HexMapping(digits) // [4 0 0 0 4 2 0 0 0 0 2 2 0 0 0 2]

	Nd := 16 // digits size
	Np := 16 // base size

	var G *bn256.G1
	// Length of our base points vector should be a power ot 2 to be used in WNLA protocol. 
	// So cause the real HVec size in circuit is `Nd+10` the nearest length is 32   
	var GVec []*bn256.G1 // len = 16
	var HVec []*bn256.G1 // len = 32

	public := &bulletproofs.ReciprocalPublic{
		G:    G,
		GVec: GVec[:Nd],
		HVec: HVec[:Nd+10],
		Nd:   Nd,
		Np:   Np,

		// Remaining points that will be used in WNLA protocol
		GVec_: GVec[Nd:],
		HVec_: HVec[Nd+10:],
	}

	private := &bulletproofs.ReciprocalPrivate{
		X:      x,                // Committed value
		M:      m,                // Corresponding multiplicities
		Digits: digits,           // Corresponding digits
		S:      NewRandScalar(), // Blinding value (secret) used for committing value as: x*G + Sx*H
	}

	VCom := public.CommitValue(private.X, private.Sx) // Value commitment: x*G + Sx*H

	// Use NewKeccakFS or your own implementation for the Fiat-Shamir heuristics.
	proof := bulletproofs.ProveRange(public, bulletproofs.NewKeccakFS(), private)

	// If err is nil -> proof is valid.
	if err := bulletproofs.VerifyRange(public, VCom, bulletproofs.NewKeccakFS(), proof); err != nil {
		panic(err)
	}
}

Weight norm linear argument (WNLA)

The wnla.go contains the implementation of weight norm linear argument protocol. This is a fundamental basis for arithmetic circuit protocol. It uses the Fiat-Shamir heuristics from fs.go to generate challenges and make protocol non-interactive.

Check the following snippet with an example of WNLA usage:

package main

import (
	"github.com/afsheenb/bulletproofs"
	"math/big"
)

func main() {
	public := bulletproofs.NewWeightNormLinearPublic(4, 2)

	// Private
	l := []*big.Int{big.NewInt(4), big.NewInt(5), big.NewInt(10), big.NewInt(1)}
	n := []*big.Int{big.NewInt(2), big.NewInt(1)}

	proof := bulletproofs.ProveWNLA(public, public.Commit(l, n), bulletproofs.NewKeccakFS(), l, n)
	if err := bulletproofs.VerifyWNLA(public, proof, public.Commit(l, n), bulletproofs.NewKeccakFS()); err != nil {
		panic(err)
	}
}

Arithmetic circuit

The circuit.go contains the implementation of BP++ arithmetic circuit protocol. It runs the WNLA protocol as the final stages of proving/verification. Uses the Fiat-Shamir heuristics from fs.go to generate challenges and make protocol non-interactive.

Check the following snippet with an example of arithmetic circuit protocol usage:

package main

import (
	"github.com/cloudflare/bn256"
	"github.com/afsheenb/bulletproofs"
)

func main() {
	public := &bulletproofs.ArithmeticCircuitPublic{
		Nm,
		Nl, // Nl = Nv * K
		Nv, // Size on any v witness vector
		Nw, // Nw = Nm + Nm + No
		No,
		K, // count of v witnesses vectors
		G,

		// points that will be used directly in circuit protocol
		GVec[:Nm],   // Nm
		HVec[:Nv+9], // Nv+9

		// Circuit definition 
		Wm, // Nm * Nw
		Wl, // Nl * Nw
		Am, // Nm
		Al, // Nl
		Fl,
		Fm,

		// Partition function
		F: func(typ bulletproofs.PartitionType, index int) *int {
			// define
			return nil
		},

		// points that will be used in WNLA protocol to make vectors 2^n len
		HVec[Nv+9:], // 2^x - (Nv+9) dimension
		GVec[Nm:],   // 2^y - Nm dimension
	}

	private := &bulletproofs.ArithmeticCircuitPrivate{
		V,  // witness vectors v, dimension k*Nv
		Sv, // witness blinding values, dimension k
		Wl, // Nm
		Wr, // Nm
		Wo, // No
	}

	// Commitments to the v witness vectors
	V := make([]*bn256.G1, public.K)
	for i := range V {
		V[i] = public.CommitCircuit(private.V[i], private.Sv[i], public.G, public.HVec)
	}

	proof := bulletproofs.ProveCircuit(public, bulletproofs.NewKeccakFS(), private)

	if err := bulletproofs.VerifyCircuit(public, V, bulletproofs.NewKeccakFS(), proof); err != nil {
		panic(err)
	}
}

Documentation

Overview

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs implements Bulletproofs++ zero-knowledge range proofs using the reciprocal argument construction with arithmetic circuits.

This package provides: - Single value range proofs (ProveRange/VerifyRange) - Batch aggregated range proofs for K values (ProveBatchRange/VerifyBatchRange) - Efficient Pedersen commitment utilities

Range proofs demonstrate that committed values lie in [0, 2^n) without revealing the values. Batch proofs aggregate multiple range proofs into a single proof for improved efficiency.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Package bulletproofs Copyright 2024 Distributed Lab. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Index

Constants

View Source
const (
	DOMAIN_CIRCUIT = "EMZA-BP++-Circuit-v1"
	DOMAIN_RANGE   = "EMZA-BP++-Range-v1"
	DOMAIN_WNLA    = "EMZA-BP++-WNLA-v1"
)

Domain separation constants for different protocols

Variables

This section is empty.

Functions

func BatchCommitValues

func BatchCommitValues(public *BatchReciprocalPublic, values []*big.Int, blindings []*big.Int) []*bn256.G1

BatchCommitValues creates Pedersen commitments for K values using the batch public parameters. Returns K commitments: V[i] = value[i]*G + blinding[i]*H for i in 0..K-1

Parameters:

  • public: Batch public parameters (uses G and H[0] for commitments)
  • values: K values to commit
  • blindings: K random blinding factors for hiding

Returns:

  • []*bn256.G1: K Pedersen commitments

func HexMapping

func HexMapping(digits []*big.Int) []*big.Int

func Keccak256

func Keccak256(data ...[]byte) []byte

Keccak256 calculates and returns the Keccak256 hash of the input data.

func NewRandPoint

func NewRandPoint() *bn256.G1

NewRandPoint creates a new random point, panicking if random generation fails This is for internal use in setup/testing where crypto failure should be fatal

func NewRandScalar

func NewRandScalar() *big.Int

NewRandScalar creates a new random scalar, panicking if random generation fails This is for internal use in setup/testing where crypto failure should be fatal

func SecureRandPoint

func SecureRandPoint() (*bn256.G1, error)

SecureRandPoint generates a cryptographically secure random group element with validation

func SecureRandScalar

func SecureRandScalar() (*big.Int, error)

SecureRandScalar generates a cryptographically secure random scalar with entropy validation Uses rejection sampling for unbiased field element generation

func UInt64Hex

func UInt64Hex(x uint64) []*big.Int

func ValidateEntropy

func ValidateEntropy(data []byte) error

ValidateEntropy performs basic entropy quality checks

func VerifyBatchRange

func VerifyBatchRange(
	public *BatchReciprocalPublic,
	V []*bn256.G1,
	fs FiatShamirEngine,
	proof *BatchReciprocalProof,
) error

VerifyBatchRange verifies an aggregated BP++ reciprocal argument range proof for K values. Returns nil if the proof is valid, error otherwise.

Architecture: Reconstructs the same concatenated circuit structure (Nm = K*Nd) used for proving. Verifies that all K values satisfy their range constraints in the aggregated proof.

Parameters:

  • public: Batch public parameters including K (number of values)
  • V: K value commitments (Pedersen commitments: V[i] = value[i]*G + blinding[i]*H)
  • fs: Fiat-Shamir engine for challenge generation (use empty engine)
  • proof: BatchReciprocalProof containing aggregated circuit proof and K reciprocal commitments

Returns:

  • nil if proof is valid
  • error with descriptive message if verification fails

func VerifyCircuit

func VerifyCircuit(public *ArithmeticCircuitPublic, V []*bn256.G1, fs FiatShamirEngine, proof *ArithmeticCircuitProof) error

VerifyCircuit verifies BP++ arithmetic circuit zero-knowledge proof using WNLA protocol. If err is nil then proof is valid. Use empty FiatShamirEngine for call.

func VerifyRange

func VerifyRange(public *ReciprocalPublic, V *bn256.G1, fs FiatShamirEngine, proof *ReciprocalProof) error

VerifyRange verifies BP++ reciprocal argument range proof on arithmetic circuits. If err is nil then proof is valid. Use empty FiatShamirEngine for call.

func VerifyWNLA

VerifyWNLA verifies the weight norm linear argument proof. If err is nil then proof is valid. Use empty FiatShamirEngine for call. Also, use the same commitment that has been used during proving.

Types

type ArithmeticCircuitPrivate

type ArithmeticCircuitPrivate struct {
	V  [][]*big.Int // k*Nv
	Sv []*big.Int   // k
	Wl []*big.Int   // Nm
	Wr []*big.Int   // Nm
	Wo []*big.Int   // No
}

type ArithmeticCircuitProof

type ArithmeticCircuitProof struct {
	CL, CR, CO, CS *bn256.G1
	WNLA           *WeightNormLinearArgumentProof
}

func ProveCircuit

ProveCircuit generates zero knowledge proof that witness satisfies BP++ arithmetic circuit. Use empty FiatShamirEngine for call.

type ArithmeticCircuitPublic

type ArithmeticCircuitPublic struct {
	Nm, Nl, Nv, Nw, No int // Nw = Nm + Nm + No (for L, R, O parts), Nl = Nv * K
	K                  int // Count of witness vectors v.
	G                  *bn256.G1
	GVec               []*bn256.G1 // Nm
	HVec               []*bn256.G1 // Nv+9

	Wm [][]*big.Int // Nm * Nw
	Wl [][]*big.Int // Nl * Nw

	Am []*big.Int // Nm
	Al []*big.Int // Nl

	Fl bool
	Fm bool

	F PartitionF

	// Vectors of points that will be used in WNLA protocol
	GVec_ []*bn256.G1 // 2^n - Nm
	HVec_ []*bn256.G1 // 2^n - (Nv+9)
}

func (*ArithmeticCircuitPublic) CommitCircuit

func (p *ArithmeticCircuitPublic) CommitCircuit(v []*big.Int, s *big.Int) *bn256.G1

CommitCircuit creates a commitment for v vector and blinding s. Com = v[0]*G + s*H[0] + <v[1:], H[9:]>

type BatchReciprocalPrivate

type BatchReciprocalPrivate struct {
	X      []*big.Int   // K committed values
	M      [][]*big.Int // K sets of multiplicities (each of length No)
	Digits [][]*big.Int // K sets of digits (each of length Nd)
	S      []*big.Int   // K blinding values (one per commitment)
}

BatchReciprocalPrivate contains private witness data for K values in batch range proofs. Each slice must have length K for proper batch proof generation.

type BatchReciprocalProof

type BatchReciprocalProof struct {
	*ArithmeticCircuitProof
	V []*bn256.G1 // K reciprocal commitments
}

BatchReciprocalProof contains the aggregated range proof for K values. V contains K reciprocal commitments (one per value).

func ProveBatchRange

ProveBatchRange proves K range proofs in a single aggregated proof using the K-witness circuit. All K values share the same range parameters (Nd, Np) from public parameter. Returns proof with K reciprocal commitments that can be verified together.

Architecture: Creates a concatenated circuit with Nm = K*Nd gates where each value uses Nd gates. Constraint matrices and wire assignments are concatenated from all K proofs.

Parameters:

  • public: Batch public parameters including K (number of values) and range parameters
  • fs: Fiat-Shamir engine for non-interactive proof generation
  • private: Private witness data for K values (values, digits, multiplicities, blindings)

Returns:

  • BatchReciprocalProof containing aggregated circuit proof and K reciprocal commitments

Use empty FiatShamirEngine for call.

type BatchReciprocalPublic

type BatchReciprocalPublic struct {
	*ReciprocalPublic
	K int // Number of values to prove (K > 0)
}

BatchReciprocalPublic extends ReciprocalPublic for batch range proofs with K>1 values. K represents the number of values to prove in a single aggregated proof.

func (*BatchReciprocalPublic) BatchCommitValues

func (p *BatchReciprocalPublic) BatchCommitValues(values []*big.Int, blindings []*big.Int) []*bn256.G1

BatchCommitValues is a convenience method on BatchReciprocalPublic for batch commitments. Returns K commitments: V[i] = value[i]*G + blinding[i]*H for i in 0..K-1

type FiatShamirEngine

type FiatShamirEngine interface {
	AddPoint(*bn256.G1) error
	AddNumber(*big.Int) error
	AddDomain(domain string) error
	AddBytes([]byte) error
	GetChallenge() *big.Int
}

func NewKeccakFS

func NewKeccakFS() FiatShamirEngine

type KeccakFS

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

func (*KeccakFS) AddBytes

func (k *KeccakFS) AddBytes(data []byte) error

func (*KeccakFS) AddDomain

func (k *KeccakFS) AddDomain(domain string) error

AddDomain adds a domain separation tag to prevent cross-protocol attacks

func (*KeccakFS) AddNumber

func (k *KeccakFS) AddNumber(v *big.Int) error

func (*KeccakFS) AddPoint

func (k *KeccakFS) AddPoint(p *bn256.G1) error

func (*KeccakFS) GetChallenge

func (k *KeccakFS) GetChallenge() *big.Int

type KeccakState

type KeccakState interface {
	hash.Hash
	Read([]byte) (int, error)
}

KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports Read to get a variable amount of data from the hash state. Read is faster than Sum because it doesn't copy the internal state, but also modifies the internal state.

func NewKeccakState

func NewKeccakState() KeccakState

NewKeccakState creates a new KeccakState

type PartitionF

type PartitionF = func(typ PartitionType, index int) *int

type PartitionType

type PartitionType int
const (
	PartitionLO PartitionType = iota
	PartitionLL
	PartitionLR
	PartitionNO
)

type ReciprocalPrivate

type ReciprocalPrivate struct {
	X      *big.Int // Committed value
	M      []*big.Int
	Digits []*big.Int
	S      *big.Int // Blinding value (secret)
}

type ReciprocalProof

type ReciprocalProof struct {
	*ArithmeticCircuitProof
	V *bn256.G1
}

func ProveRange

func ProveRange(public *ReciprocalPublic, fs FiatShamirEngine, private *ReciprocalPrivate) *ReciprocalProof

ProveRange proves BP++ reciprocal argument range proof on arithmetic circuits. Returns range proof. Use empty FiatShamirEngine for call.

type ReciprocalPublic

type ReciprocalPublic struct {
	G      *bn256.G1
	GVec   []*bn256.G1 // Nm
	HVec   []*bn256.G1 // Nv+9
	Nd, Np int

	// Vectors of points that will be used in WNLA protocol
	GVec_ []*bn256.G1 // 2^n - Nm
	HVec_ []*bn256.G1 // 2^n - (Nv+9)
}

ReciprocalPublic dimensions: Nd - count of private proles (size of committed value), Np - count of public poles (number system base). Nm = Nd, No = Np Nv = 1 + Nd G and HVec[0] will be used for the value commitment: VCom = value*G + blinding*HVec[0]

func (*ReciprocalPublic) CommitPoles

func (p *ReciprocalPublic) CommitPoles(r []*big.Int, s *big.Int) *bn256.G1

CommitPoles creates a commitment to reciprocal poles r with blinding factor s. Returns commitment C = s*H[0] + sum(r[i]*H[9+i]) using reciprocal generators.

func (*ReciprocalPublic) CommitValue

func (p *ReciprocalPublic) CommitValue(v *big.Int, s *big.Int) *bn256.G1

CommitValue creates a Pedersen commitment to value v with blinding factor s. Returns commitment V = v*G + s*H where G and H[0] are public generators.

type WeightNormLinearArgumentProof

type WeightNormLinearArgumentProof struct {
	R, X []*bn256.G1
	L, N []*big.Int
}

WeightNormLinearArgumentProof contains the proof of knowledge of vectors L, N for corresponding commitment C (is not included into the proof structure).

func ProveWNLA

ProveWNLA generates zero knowledge proof of knowledge of two vectors l and n that satisfies the commitment C (see WeightNormLinearPublic.Commit() function). Use empty FiatShamirEngine for call.

type WeightNormLinearPublic

type WeightNormLinearPublic struct {
	G          *bn256.G1
	GVec, HVec []*bn256.G1
	C          []*big.Int
	Ro, Mu     *big.Int // mu = ro^2
}

WeightNormLinearPublic contains the public values to be used in weight norm linear argument proof. The GVec and HVec sizes are recommended to be a powers of 2 and equal to the `n` and `l` private vector sizes.

func NewWeightNormLinearPublic

func NewWeightNormLinearPublic(lLen int, nLen int) *WeightNormLinearPublic

func (*WeightNormLinearPublic) CommitWNLA

func (p *WeightNormLinearPublic) CommitWNLA(l []*big.Int, n []*big.Int) *bn256.G1

CommitWNLA creates a commitment for vectors n, l based on public parameters p. Commit(l, n) = v*G + <l, H> + <n, G> where v = <c, l> + |n^2|_mu

Jump to

Keyboard shortcuts

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