Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make stateproof verifier snark friendly #3895

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ebd1016
change coin filps to be shake(sumhash(seed))
Feb 16, 2022
5446cae
add sequence of coin positions to the cert
Mar 10, 2022
4363c91
compute CC security using implied provenWeight
Mar 13, 2022
3ee4616
create a log2 appr func
Mar 15, 2022
6f4de56
fix stateproof message issues.
Mar 15, 2022
92ce531
remove proven weight form the coin hash
Mar 16, 2022
a21f7b7
fix cc test
Mar 17, 2022
f8168fe
use reject sampling in coin hash.
Mar 17, 2022
0afd674
use logarithmic approximation
Mar 27, 2022
b548344
refactoring
Mar 28, 2022
27c4e11
builder uses same appox function
Mar 31, 2022
b4c9be4
comments and doc
Mar 31, 2022
4ce22b6
handle negative value in number of reveals equation
Apr 4, 2022
40a9de2
add lnProvenWe to coinhash
Apr 10, 2022
2f275f3
fixed hash representation for coinhash
Apr 10, 2022
c76184b
fix CC benchmark
Apr 10, 2022
ac4df71
refactor
Apr 10, 2022
8911552
remove old numberofreveals code
Apr 10, 2022
593b62f
change secKQ to 256
Apr 11, 2022
e794180
fix CRs
Apr 12, 2022
c13ce05
CR fixes + rename
Apr 13, 2022
106e6f4
more CR fix
Apr 14, 2022
a3507ad
refactor the trusted params on the verifer.
Apr 14, 2022
3789b44
more refactoring
Apr 17, 2022
ec0543c
fix flaky test
Apr 17, 2022
6e35aad
remove Param structure
Apr 17, 2022
867d2cc
more fixes
Apr 17, 2022
cffa28a
update falcon lib + use byte as salt version
Apr 19, 2022
dd70755
add coinhash kat generator
Apr 20, 2022
485b44d
fix some CR comments
Apr 26, 2022
2cea300
clear out some documentation
Apr 27, 2022
2f03f65
Apply suggestions from code review
id-ms Apr 27, 2022
b566381
fix comments
Apr 28, 2022
ab2a2ff
refactor rejection sampling
Apr 28, 2022
112e5e7
CR fix
May 1, 2022
b7f38d9
refactoring
May 1, 2022
25a33d3
fix comments
May 3, 2022
30ee541
reduce the bytes allocated for stateproof message
May 3, 2022
ed9f141
Apply suggestions from code review
id-ms May 4, 2022
5ae415b
fix test since stateproof message hash was reduce
May 4, 2022
32a8164
fix CR comments
May 8, 2022
9c6a06a
more refactoring
May 8, 2022
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
Prev Previous commit
Next Next commit
builder uses same appox function
  • Loading branch information
algoidan committed Apr 17, 2022
commit 27c4e110a8a45ae4ba53d010fe6c21a65ea498e1
61 changes: 1 addition & 60 deletions crypto/compactcert/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package compactcert

import (
"fmt"

"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merklearray"
"github.com/algorand/go-algorand/crypto/merklesignature"
Expand Down Expand Up @@ -163,64 +162,6 @@ again:
goto again
}

// numReveals computes the number of reveals necessary to achieve the desired
// security parameters. See section 8 of the ``Compact Certificates''
// document for the analysis.
//
// numReveals is the smallest number that satisfies
//
// 2^-k >= 2^q * (provenWeight / signedWeight) ^ numReveals
//
// which is equivalent to the following:
//
// signedWeight ^ numReveals >= 2^(k+q) * provenWeight ^ numReveals
//
// To ensure that rounding errors do not reduce the security parameter,
// we compute the left-hand side with rounding-down, and compute the
// right-hand side with rounding-up.
func numReveals(signedWeight uint64, provenWeight uint64, secKQ uint64, bound uint64) (uint64, error) {
n := uint64(0)

sw := &bigFloatDn{}
err := sw.setu64(signedWeight)
if err != nil {
return 0, err
}

pw := &bigFloatUp{}
err = pw.setu64(provenWeight)
if err != nil {
return 0, err
}

lhs := &bigFloatDn{}
err = lhs.setu64(1)
if err != nil {
return 0, err
}

rhs := &bigFloatUp{}
rhs.setpow2(int32(secKQ))

for {
if lhs.ge(rhs) {
return n, nil
}

if n >= bound {
return 0, fmt.Errorf("numReveals(%d, %d, %d) > %d", signedWeight, provenWeight, secKQ, bound)
}

lhs.mul(sw)
rhs.mul(pw)
n++
}
}

func (p *Params) numReveals(signedWeight uint64) (uint64, error) {
return numReveals(signedWeight, p.ProvenWeightThreshold, p.SecKQ, MaxReveals)
}

// Build returns a compact certificate, if the builder has accumulated
// enough signatures to construct it.
func (b *Builder) Build() (*Cert, error) {
Expand All @@ -229,7 +170,7 @@ func (b *Builder) Build() (*Cert, error) {
}

if b.signedWeight <= b.Params.ProvenWeightThreshold {
return nil, fmt.Errorf("not enough signed weight: %d <= %d", b.signedWeight, b.Params.ProvenWeightThreshold)
return nil, fmt.Errorf("%w: %d <= %d", ErrSignedWeightLessThanProvenWeight, b.signedWeight, b.Params.ProvenWeightThreshold)
}

// Commit to the sigs array
Expand Down
21 changes: 0 additions & 21 deletions crypto/compactcert/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,24 +554,3 @@ func BenchmarkBuildVerify(b *testing.B) {
}
})
}

func BenchmarkNumReveals(b *testing.B) {
billion := uint64(1000 * 1000 * 1000)
microalgo := uint64(1000 * 1000)
provenWeight := 100 * billion * microalgo
signedWeight := 110 * billion * microalgo
secKQ := uint64(compactCertSecKQForTests)
bound := uint64(1000)

nr, err := numReveals(signedWeight, provenWeight, secKQ, bound)
if nr < 900 {
b.Errorf("numReveals(%d, %d, %d) = %d < 900", signedWeight, provenWeight, secKQ, nr)
}

for i := 0; i < b.N; i++ {
_, err = numReveals(signedWeight, provenWeight, secKQ, bound)
if err != nil {
b.Error(err)
}
}
}
104 changes: 2 additions & 102 deletions crypto/compactcert/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,14 @@ package compactcert
import (
"errors"
"fmt"
"math"
"math/big"
"math/bits"

"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merklearray"
)

// Errors for the CompactCert verifier
var (
ErrCoinNotInRange = errors.New("coin is not within slot weight range")
ErrNoRevealInPos = errors.New("no reveal for position")
ErrSignedWeightLessThanProvenWeight = errors.New("signed weight is less than or equal to proven weight")
ErrTooManyReveals = errors.New("too many reveals in cert")
ErrZeroSignedWeight = errors.New("signed weight can not be zero")
ErrZeroProvenWeightThreshold = errors.New("proven weight can not be zero")
ErrInsufficientImpliedProvenWeight = errors.New("signed weight and number of reveals yield insufficient proven weight")
ErrCoinNotInRange = errors.New("coin is not within slot weight range")
ErrNoRevealInPos = errors.New("no reveal for position")
)

// Verifier is used to verify a compact certificate.
Expand All @@ -59,97 +50,6 @@ func MkVerifier(p Params, partcom crypto.GenericDigest) *Verifier {
}
}

// verifyWeights makes sure that the number of reveals in the cert is correct with respect
// to the signedWeight and a provenWeight threshold.
// According to the security analysis the number of reveals is given by the following:
//
// numReveals is the smallest number that satisfies
// 2^-k >= 2^q * (provenWeight / signedWeight) ^ numReveals
//
// in order to make the verification SNARK friendly we will not compute the exact number of reveals (as it is done in the build)
// Alternatively, we would use a lower bound on the implied provenWeight using a given numReveals and signedWeight .
// i.e we need to verify that:
// numReveals*(log2(signedWeight)-log2(provenWeightThreshold)) >= k+q
// In addition, we would like to use a friendly log2 approximation. it is sufficient to verify the following inequality:
//
// numReveals*(3*2^b*(signedWeight^2-2^2d)+d(T-1)*Y) >= ((k+q)*T+numReveals*P)*Y
// where signedWeight/(2^d) >=1 for some integer d>=0, p = P/(2^b) >= ln(provenWeightThreshold), t = T/(2^b) >= ln(2) >= (T-1)/(2^b)
// for some integers P,T >= 0 and b=16
func (v *Verifier) verifyWeights(signedWeight uint64, numOfReveals uint64) error {
if numOfReveals > MaxReveals {
return ErrTooManyReveals
}

if signedWeight == 0 {
return ErrZeroSignedWeight
}

if v.ProvenWeightThreshold == 0 {
return ErrZeroProvenWeightThreshold
}

if signedWeight <= v.ProvenWeightThreshold {
return fmt.Errorf("%w - signed weight %d <= proven weight %d", ErrSignedWeightLessThanProvenWeight, signedWeight, v.ProvenWeightThreshold)
}

// find d s.t 2^(d+1) >= signedWeight >= 2^(d)
d := uint64(bits.Len64(signedWeight)) - 1

signedWtPower2 := &big.Int{}
signedWtPower2.SetUint64(signedWeight)
signedWtPower2.Mul(signedWtPower2, signedWtPower2)

// Y = signedWt^2 + 4*2^d*signedWt +2^2d
tmp := &big.Int{}
tmp.SetUint64(4)
tmp.Mul(tmp, (&big.Int{}).SetUint64(1<<d))
tmp.Mul(tmp, (&big.Int{}).SetUint64(signedWeight)) //tmp = 4*2^d*signedWt

y := &big.Int{}
y.Add(signedWtPower2, tmp)
y.Add(y, (&big.Int{}).SetUint64(1<<(2*d)))

// a = 3*2^b*(signedWt^2-2^2d) + d*(T-1)*Y
tmp.SetUint64(d)
tmp.Mul(tmp, (&big.Int{}).SetUint64(ln2IntApproximation-1))
tmp.Mul(tmp, y) //tmp = d*(T-1)*Y

a := &big.Int{}
a.Sub(signedWtPower2, (&big.Int{}).SetUint64(1<<(2*d)))
a.Mul(a, (&big.Int{}).SetUint64(3))
a.Mul(a, (&big.Int{}).SetUint64(precisionBits))
a.Add(a, tmp)

// left = NumReveals*a
left := &big.Int{}
left.Mul(a, (&big.Int{}).SetUint64(numOfReveals))

// right = (secParam*t + NumReveals*P)*Y
// tmp = secParam*t
tmp.SetUint64(v.SecKQ)
tmp.Mul(tmp, (&big.Int{}).SetUint64(ln2IntApproximation))

right := &big.Int{}
right.Mul((&big.Int{}).SetUint64(v.lnProvenWeightThreshold), (&big.Int{}).SetUint64(numOfReveals))
right.Add(tmp, right)
right.Mul(right, y)

if left.Cmp(right) < 0 {
return ErrInsufficientImpliedProvenWeight
}

return nil
}

func lnIntApproximation(x uint64, precisionBits uint64) uint64 {
if x == 0 {
return 0
}
result := math.Log(float64(x))
expendWithPer := result * float64(precisionBits)
return uint64(math.Ceil(expendWithPer))
}

// Verify checks if c is a valid compact certificate for the message
// and participants that were used to construct the Verifier.
func (v *Verifier) Verify(c *Cert) error {
Expand Down
141 changes: 0 additions & 141 deletions crypto/compactcert/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
package compactcert

import (
"math"
"testing"

"github.com/stretchr/testify/require"

"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merklesignature"
"github.com/algorand/go-algorand/test/partitiontest"
)
Expand Down Expand Up @@ -87,142 +85,3 @@ func TestVerifyBadSignature(t *testing.T) {
err = verifier.Verify(cert)
a.ErrorIs(err, merklesignature.ErrSignatureSchemeVerificationFailed)
}

func TestVerifyMaxNumberOfReveals(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

signedWeight := uint64(10)
provenWeight := uint64(10)

param := Params{SecKQ: 128, ProvenWeightThreshold: provenWeight}
verifier := MkVerifier(param, crypto.GenericDigest{})
err := verifier.verifyWeights(signedWeight, MaxReveals+1)
a.ErrorIs(err, ErrTooManyReveals)
}

func TestVerifySignedWeightLessThanProvenWeight(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

signedWeight := uint64(1 << 10)
provenWeight := uint64(1<<10 + 1)

param := Params{SecKQ: compactCertSecKQForTests, ProvenWeightThreshold: provenWeight}
verifier := MkVerifier(param, crypto.GenericDigest{})
err := verifier.verifyWeights(signedWeight, compactCertSecKQForTests)
a.ErrorIs(err, ErrSignedWeightLessThanProvenWeight)
}

func TestVerifyImpliedProvenBiggerThanThreshold(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

signedWeight := uint64(1 << 11)
provenWeight := uint64(1<<10 - 1)

numOfReveals, err := numReveals(signedWeight, provenWeight, compactCertSecKQForTests, MaxReveals)

param := Params{SecKQ: compactCertSecKQForTests, ProvenWeightThreshold: provenWeight}
verifier := MkVerifier(param, crypto.GenericDigest{})
err = verifier.verifyWeights(signedWeight, numOfReveals)
a.NoError(err)
}

func TestVerifyImpliedProvenBiggerThanThresholdApproximationError(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

signedWeight := uint64(1 << 11)
provenWeight := uint64(1 << 10)

numOfReveals, err := numReveals(signedWeight, provenWeight, compactCertSecKQForTests, MaxReveals)

param := Params{SecKQ: compactCertSecKQForTests, ProvenWeightThreshold: provenWeight}
verifier := MkVerifier(param, crypto.GenericDigest{})
err = verifier.verifyWeights(signedWeight, numOfReveals)
a.ErrorIs(err, ErrInsufficientImpliedProvenWeight)
}

func TestLnWithPrecision(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

for i := 1; i < 32; i++ {
exp := 1 << i
val := lnIntApproximation(2, uint64(exp))
a.GreaterOrEqual(float64(val)/float64(exp), math.Log(2))
a.Greater(math.Log(2), float64(val-1)/float64(exp))
}

a.Equal(ln2IntApproximation, lnIntApproximation(2, precisionBits))
}

func TestVerifyLimits(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

signedWeight := uint64(0)
provenWeight := uint64(1<<10 - 1)

param := Params{SecKQ: compactCertSecKQForTests, ProvenWeightThreshold: provenWeight}
verifier := MkVerifier(param, crypto.GenericDigest{})
err := verifier.verifyWeights(signedWeight, 130)
a.ErrorIs(err, ErrZeroSignedWeight)

signedWeight = 101
provenWeight = 0

param = Params{SecKQ: compactCertSecKQForTests, ProvenWeightThreshold: provenWeight}
verifier = MkVerifier(param, crypto.GenericDigest{})
err = verifier.verifyWeights(signedWeight, 0)
a.ErrorIs(err, ErrZeroProvenWeightThreshold)
}

func TestNumReveals(t *testing.T) {
partitiontest.PartitionTest(t)

billion := uint64(1000 * 1000 * 1000)
microalgo := uint64(1000 * 1000)
provenWeight := 2 * billion * microalgo
secKQ := uint64(compactCertSecKQForTests)
bound := uint64(1000)

for i := uint64(3); i < 10; i++ {
signedWeight := i * billion * microalgo
n, err := numReveals(signedWeight, provenWeight, secKQ, bound)
require.NoError(t, err)
if n < 50 || n > 300 {
t.Errorf("numReveals(%d, %d, %d) = %d looks suspect",
signedWeight, provenWeight, secKQ, n)
}

param := Params{SecKQ: compactCertSecKQForTests, ProvenWeightThreshold: provenWeight}
verifier := MkVerifier(param, crypto.GenericDigest{})
err = verifier.verifyWeights(signedWeight, n)
require.NoError(t, err)
}
}

func BenchmarkWeight(b *testing.B) {
billion := uint64(1000 * 1000 * 1000)
microalgo := uint64(1000 * 1000)
provenWeight := 100 * billion * microalgo
signedWeight := 110 * billion * microalgo
secKQ := uint64(compactCertSecKQForTests)
bound := uint64(1000)

nr, err := numReveals(signedWeight, provenWeight, secKQ, bound)
if nr < 900 {
b.Errorf("numReveals(%d, %d, %d) = %d < 900", signedWeight, provenWeight, secKQ, nr)
}
param := Params{SecKQ: secKQ, ProvenWeightThreshold: provenWeight}
verifier := MkVerifier(param, crypto.GenericDigest{})

for i := 0; i < b.N; i++ {
err = verifier.verifyWeights(signedWeight, nr)
if err != nil {
b.Error(err)
}
}
}
Loading