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

vochain: refactor proof verification logic #1246

Merged
merged 1 commit into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
36 changes: 22 additions & 14 deletions api/census_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,18 @@ func TestCensusProof(t *testing.T) {
ProcessId: electionID,
CensusRoot: censusData.CensusID,
CensusOrigin: models.CensusOrigin_OFF_CHAIN_TREE_WEIGHTED,
EnvelopeType: &models.EnvelopeType{},
},
&models.Proof{
Payload: &models.Proof_Arbo{
Arbo: &models.ProofArbo{
Type: models.ProofArbo_BLAKE2B,
Siblings: censusData.CensusProof,
AvailableWeight: censusData.Value,
VoteWeight: censusData.Value,
&models.VoteEnvelope{
ProcessId: electionID,
Proof: &models.Proof{
Payload: &models.Proof_Arbo{
Arbo: &models.ProofArbo{
Type: models.ProofArbo_BLAKE2B,
Siblings: censusData.CensusProof,
AvailableWeight: censusData.Value,
VoteWeight: censusData.Value,
},
},
},
},
Expand Down Expand Up @@ -329,14 +333,18 @@ func TestCensusZk(t *testing.T) {
ProcessId: electionID,
CensusRoot: censusData.CensusID,
CensusOrigin: models.CensusOrigin_OFF_CHAIN_TREE_WEIGHTED,
EnvelopeType: &models.EnvelopeType{},
},
&models.Proof{
Payload: &models.Proof_Arbo{
Arbo: &models.ProofArbo{
Type: models.ProofArbo_POSEIDON,
Siblings: censusData.CensusProof,
AvailableWeight: censusData.Value,
VoteWeight: censusData.Value,
&models.VoteEnvelope{
ProcessId: electionID,
Proof: &models.Proof{
Payload: &models.Proof_Arbo{
Arbo: &models.ProofArbo{
Type: models.ProofArbo_POSEIDON,
Siblings: censusData.CensusProof,
AvailableWeight: censusData.Value,
VoteWeight: censusData.Value,
},
},
},
},
Expand Down
3 changes: 1 addition & 2 deletions apiclient/vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,10 @@ func (cl *HTTPclient) Vote(v *VoteData) (types.HexBytes, error) {
return nil, err
}
// include vote nullifier and the encoded proof in a VoteEnvelope
nullifier, err := proof.ExtractPubSignal("nullifier")
vote.Nullifier, err = proof.Nullifier()
if err != nil {
return nil, err
}
vote.Nullifier = nullifier.Bytes()
vote.Proof = &models.Proof{
Payload: &models.Proof_ZkSnark{
ZkSnark: protoProof,
Expand Down
2 changes: 1 addition & 1 deletion crypto/ethereum/vocdoni_sik.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (k *SignKeys) AccountSIKnullifier(electionID, secret []byte) ([]byte, error
seed = append(seed, big.NewInt(0))
}
// encode the election id for circom and include it into the nullifier
seed = append(seed, util.BytesToArbo(electionID)...)
seed = append(seed, util.BytesToArboSplit(electionID)...)
// calculate the poseidon image --> H(signature + secret + electionId)
hash, err := poseidon.Hash(seed)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions crypto/zk/circuit/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ func GenerateCircuitInput(p CircuitInputsParameters) (*CircuitInputs, error) {
return nil, err
}
return &CircuitInputs{
ElectionId: util.BytesToArboStr(p.ElectionId),
ElectionId: util.BytesToArboSplitStr(p.ElectionId),
Nullifier: new(big.Int).SetBytes(nullifier).String(),
AvailableWeight: p.AvailableWeight.String(),
VoteHash: util.BytesToArboStr(p.AvailableWeight.Bytes()),
VoteHash: util.BytesToArboSplitStr(p.AvailableWeight.Bytes()),
SIKRoot: arbo.BytesToBigInt(p.SIKRoot).String(),
CensusRoot: arbo.BytesToBigInt(p.CensusRoot).String(),

Expand Down
2 changes: 1 addition & 1 deletion crypto/zk/circuit/inputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestGenerateCircuitInput(t *testing.T) {
// mock the availableWeight
availableWeight := new(big.Int).SetUint64(10)
// calc vote hash
voteHash := util.BytesToArboStr(availableWeight.Bytes())
voteHash := util.BytesToArboSplitStr(availableWeight.Bytes())
p4u marked this conversation as resolved.
Show resolved Hide resolved
// decode the test root
hexTestRoot, err := hex.DecodeString(testRoot)
c.Assert(err, qt.IsNil)
Expand Down
43 changes: 0 additions & 43 deletions crypto/zk/prover/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@ package prover
import (
"encoding/json"
"fmt"
"math/big"

"github.com/iden3/go-rapidsnark/prover"
"github.com/iden3/go-rapidsnark/types"
"github.com/iden3/go-rapidsnark/verifier"
"github.com/iden3/go-rapidsnark/witness"
"go.vocdoni.io/dvote/crypto/zk/circuit"
"go.vocdoni.io/dvote/tree/arbo"
)

// TODO: Refactor the error handling to include the trace of the original error
Expand Down Expand Up @@ -82,46 +79,6 @@ func (p *Proof) Bytes() ([]byte, []byte, error) {
return proofData, pubSignals, nil
}

// ExtractPubSignal decodes the requested public signal (identified by a string: "nullifier", "sikRoot", etc)
// from the current proof and returns it as a big.Int.
func (p *Proof) ExtractPubSignal(id string) (*big.Int, error) {
// Check if the current proof contains public signals and it contains the
// correct number of positions.
if p.PubSignals == nil || len(p.PubSignals) != len(circuit.Global().Config.PublicSignals) {
return nil, ErrPublicSignalFormat
}
idx, found := circuit.Global().Config.PublicSignals[id]
if !found {
return nil, ErrPubSignalNotFound
}
s := p.PubSignals[idx]
// Parse it into a big.Int
i, ok := new(big.Int).SetString(s, 10)
if !ok {
return nil, ErrParsingProofSignal
}
return i, nil

}

// SIKRoot function returns the SIKRoot included into the current proof.
func (p *Proof) SIKRoot() ([]byte, error) {
sikRoot, err := p.ExtractPubSignal("sikRoot")
if err != nil {
return nil, err
}
return arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), sikRoot), nil
}

// Nullifier function returns the Nullifier included into the current proof.
func (p *Proof) Nullifier() ([]byte, error) {
nullifier, err := p.ExtractPubSignal("nullifier")
if err != nil {
return nil, err
}
return nullifier.Bytes(), nil
}

// calcWitness perform the witness calculation using go-rapidsnark library based
// on wasm version of the circuit and inputs provided. To provide the arguments
// into the correct way, just read the content of wasm binary and inputs JSON
Expand Down
112 changes: 112 additions & 0 deletions crypto/zk/prover/pubsignals.go
p4u marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package prover

import (
"math/big"

"go.vocdoni.io/dvote/crypto/zk/circuit"
"go.vocdoni.io/dvote/tree/arbo"
"go.vocdoni.io/dvote/util"
)

// extractPubSignal decodes the requested public signal (identified by a string: "nullifier", "sikRoot", etc)
// from the current proof and returns it as a big.Int.
func (p *Proof) extractPubSignal(id string) (string, error) {
// Check if the current proof contains public signals and it contains the
// correct number of positions.
if p.PubSignals == nil || len(p.PubSignals) != len(circuit.Global().Config.PublicSignals) {
return "", ErrPublicSignalFormat
}
idx, found := circuit.Global().Config.PublicSignals[id]
if !found {
return "", ErrPubSignalNotFound
}
return p.PubSignals[idx], nil
}

// stringToBigInt converts a string into a standard big.Int.
func stringToBigInt(s string) (*big.Int, error) {
i, ok := new(big.Int).SetString(s, 10)
if !ok {
return nil, ErrParsingProofSignal
}
return i, nil
}

// ElectionID returns the ElectionID used as public input to generate the proof.
func (p *Proof) ElectionID() ([]byte, error) {
electionID1str, err := p.extractPubSignal("electionId[0]")
if err != nil {
return nil, err
}
electionID2str, err := p.extractPubSignal("electionId[1]")
if err != nil {
return nil, err
}
return util.SplittedArboStrToBytes(electionID1str, electionID2str), nil
}

// VoteHash returns the VoteHash included into the current proof.
func (p *Proof) VoteHash() ([]byte, error) {
voteHash1str, err := p.extractPubSignal("voteHash[0]")
if err != nil {
return nil, err
}
voteHash2str, err := p.extractPubSignal("voteHash[1]")
if err != nil {
return nil, err
}
return util.SplittedArboStrToBytes(voteHash1str, voteHash2str), nil
}

// CensusRoot returns the CensusRoot included into the current proof.
func (p *Proof) CensusRoot() ([]byte, error) {
censusRoot, err := p.extractPubSignal("censusRoot")
if err != nil {
return nil, err
}
bi, err := stringToBigInt(censusRoot)
if err != nil {
return nil, err
}
return arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), bi), nil
}

// VoteWeight returns the VoteWeight included into the current proof.
func (p *Proof) VoteWeight() (*big.Int, error) {
weight, err := p.extractPubSignal("voteWeight")
if err != nil {
return nil, err
}
return stringToBigInt(weight)
}

// AvailableWeight returns the AvailableWeight included into the current proof (not implemented).
func (p *Proof) AvailableWeight() (*big.Int, error) {
panic("not implemented")
}

// Nullifier returns the Nullifier included into the current proof.
func (p *Proof) Nullifier() ([]byte, error) {
nullifier, err := p.extractPubSignal("nullifier")
if err != nil {
return nil, err
}
bi, err := stringToBigInt(nullifier)
if err != nil {
return nil, err
}
return bi.Bytes(), err
}

// SIKRoot function returns the SIKRoot included into the current proof.
func (p *Proof) SIKRoot() ([]byte, error) {
sikRoot, err := p.extractPubSignal("sikRoot")
if err != nil {
return nil, err
}
bi, err := stringToBigInt(sikRoot)
if err != nil {
return nil, err
}
return arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), bi), nil
}
26 changes: 21 additions & 5 deletions util/zk.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,35 @@ func BigToFF(iv *big.Int) *big.Int {
return z.Mod(iv, bn254BaseField)
}

// BytesToArbo calculates the sha256 hash (32 bytes) of the slice of bytes
// BytesToArboSplit calculates the sha256 hash (32 bytes) of the slice of bytes
// provided. Then, splits the hash into a two parts of 16 bytes, swap the
// endianess of that parts and encodes they into a two big.Int's.
func BytesToArbo(input []byte) []*big.Int {
func BytesToArboSplit(input []byte) []*big.Int {
hash := sha256.Sum256(input)
return []*big.Int{
new(big.Int).SetBytes(arbo.SwapEndianness(hash[:16])),
new(big.Int).SetBytes(arbo.SwapEndianness(hash[16:])),
}
}

// BytesToArboStr function wraps BytesToArbo to return the input as []string.
func BytesToArboStr(input []byte) []string {
arboBytes := BytesToArbo(input)
// BytesToArboSplitStr function wraps BytesToArbo to return the input as []string.
func BytesToArboSplitStr(input []byte) []string {
arboBytes := BytesToArboSplit(input)
return []string{arboBytes[0].String(), arboBytes[1].String()}
}

// SplittedArboToBytes function receives a slice of big.Int's and returns the
func SplittedArboToBytes(input1, input2 *big.Int) []byte {
b1 := arbo.SwapEndianness(input1.Bytes())
b2 := arbo.SwapEndianness(input2.Bytes())
return append(b1, b2...)
}

// SplittedArboStrToBytes function wraps SplittedArboToBytes to return the input as []byte
func SplittedArboStrToBytes(input1, input2 string) []byte {
b1 := new(big.Int)
b1.SetString(input1, 10)
b2 := new(big.Int)
b2.SetString(input2, 10)
return SplittedArboToBytes(b1, b2)
}
20 changes: 19 additions & 1 deletion util/zk_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package util

import (
"bytes"
"crypto/sha256"
"math/big"
"testing"

Expand All @@ -11,10 +13,26 @@ func TestBytesToArboStr(t *testing.T) {
c := qt.New(t)

input := new(big.Int).SetInt64(1233)
encoded := BytesToArboStr(input.Bytes())
encoded := BytesToArboSplitStr(input.Bytes())
expected := []string{
"145749485520268040037154566173721592631",
"243838562910071029186006881148627719363",
}
c.Assert(encoded, qt.DeepEquals, expected)
}

func TestConversionConsistency(t *testing.T) {
originalInput := []byte("test input")

// Convert to strings
strParts := BytesToArboSplitStr(originalInput)

// Convert strings back to bytes
reconstructedBytes := SplittedArboStrToBytes(strParts[0], strParts[1])

// Hash the original input to compare with reconstructed bytes
expectedHash := sha256.Sum256(originalInput)

// Check if reconstructed bytes match the hash of the original input
qt.Assert(t, bytes.Equal(reconstructedBytes, expectedHash[:]), qt.IsTrue)
}
Loading
Loading