Skip to content

Commit

Permalink
vochain: get nullifier from proof on zk voting elections
Browse files Browse the repository at this point in the history
Ignore the voteEnvelope.Nullifier since it is duplicated.
This change produces a soft-fork.
  • Loading branch information
p4u committed Jan 3, 2024
1 parent 19daf48 commit bc5610a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 18 deletions.
9 changes: 6 additions & 3 deletions config/forks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package config

// ForksCfg allows applying softforks at specified heights
type ForksCfg struct {
VoceremonyForkBlock uint32
VoceremonyForkBlock uint32
NullifierFromZkProof uint32
}

// Forks is a map of chainIDs
Expand All @@ -11,10 +12,12 @@ var Forks = map[string]*ForksCfg{
VoceremonyForkBlock: 217200, // estimated 2023-12-05T11:33:31.426638381Z
},
"vocdoni/STAGE/9": {
VoceremonyForkBlock: 250000, // estimated 2023-12-11T12:09:00.917676214Z
VoceremonyForkBlock: 250000, // estimated 2023-12-11T12:09:00.917676214Z
NullifierFromZkProof: 439000, // estimated 2024-01-03T12:09:30.009477164Z
},
"vocdoni/LTS/1.2": {
VoceremonyForkBlock: 400200, // estimated 2023-12-12T09:09:31.511245938Z
VoceremonyForkBlock: 400200, // estimated 2023-12-12T09:09:31.511245938Z
NullifierFromZkProof: 575800, // estimated 2024-01-03T12:09:30.009477164Z
},
}

Expand Down
9 changes: 9 additions & 0 deletions crypto/zk/prover/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ func (p *Proof) SIKRoot() ([]byte, error) {
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
56 changes: 41 additions & 15 deletions vochain/transaction/vote_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"errors"
"fmt"

"go.vocdoni.io/dvote/config"
"go.vocdoni.io/dvote/crypto/ethereum"
"go.vocdoni.io/dvote/crypto/zk"
"go.vocdoni.io/dvote/crypto/zk/circuit"
"go.vocdoni.io/dvote/crypto/zk/prover"
"go.vocdoni.io/dvote/log"
vstate "go.vocdoni.io/dvote/vochain/state"
"go.vocdoni.io/dvote/vochain/transaction/vochaintx"
Expand Down Expand Up @@ -95,12 +97,12 @@ func (t *TransactionHandler) VoteTxCheck(vtx *vochaintx.Tx, forCommit bool) (*vs
} else { // if vote not in cache, initialize it
// Initialize the vote based on the envelope type
if process.GetEnvelopeType().Anonymous {
vote = initializeZkVote(voteEnvelope, height)
vote, err = initializeZkVote(voteEnvelope, height)
} else {
vote, err = initializeSignedVote(voteEnvelope, vtx.SignedBody, vtx.Signature, height)
if err != nil {
return nil, err
}
}
if err != nil {
return nil, err
}

// if process encrypted, check the vote is encrypted (includes at least one key index)
Expand Down Expand Up @@ -136,14 +138,9 @@ func (t *TransactionHandler) VoteTxCheck(vtx *vochaintx.Tx, forCommit bool) (*vs
return nil, fmt.Errorf("anonymous voting not supported, missing zk circuits data")
}
// get snark proof from vote envelope
proofZkSNARK := voteEnvelope.Proof.GetZkSnark()
if proofZkSNARK == nil {
return nil, fmt.Errorf("zkSNARK proof is empty")
}
// parse the ZkProof protobuf to prover.Proof
proof, err := zk.ProtobufZKProofToProverProof(proofZkSNARK)
proof, err := zkProofFromEnvelope(voteEnvelope)
if err != nil {
return nil, fmt.Errorf("failed on zk.ProtobufZKProofToCircomProof: %w", err)
return nil, err
}
// get sikroot from the proof
proofSIKRoot, err := proof.SIKRoot()
Expand All @@ -154,6 +151,13 @@ func (t *TransactionHandler) VoteTxCheck(vtx *vochaintx.Tx, forCommit bool) (*vs
if t.state.ExpiredSIKRoot(proofSIKRoot) {
return nil, fmt.Errorf("expired sik root provided, generate the proof again")
}

// soft-fork: get nullifier from proof publicSignals
if nullifierCheckForkBlock := config.ForksForChainID(t.state.ChainID()).NullifierFromZkProof; nullifierCheckForkBlock > 0 &&
t.state.CurrentHeight() < nullifierCheckForkBlock {
vote.Nullifier = voteEnvelope.Nullifier
}

// get vote weight from proof publicSignals
vote.Weight, err = proof.ExtractPubSignal("voteWeight")
if err != nil {
Expand All @@ -162,7 +166,7 @@ func (t *TransactionHandler) VoteTxCheck(vtx *vochaintx.Tx, forCommit bool) (*vs
log.Debugw("new vote",
"type", "zkSNARK",
"weight", vote.Weight,
"nullifier", fmt.Sprintf("%x", voteEnvelope.Nullifier),
"nullifier", fmt.Sprintf("%x", vote.Nullifier),
"electionID", fmt.Sprintf("%x", voteEnvelope.ProcessId),
)
// verify the proof with the circuit verification key
Expand Down Expand Up @@ -200,15 +204,37 @@ func (t *TransactionHandler) VoteTxCheck(vtx *vochaintx.Tx, forCommit bool) (*vs
return vote, nil
}

func zkProofFromEnvelope(voteEnvelope *models.VoteEnvelope) (*prover.Proof, error) {
proofZkSNARK := voteEnvelope.Proof.GetZkSnark()
if proofZkSNARK == nil {
return nil, fmt.Errorf("zkSNARK proof is empty")
}
// parse the ZkProof protobuf to prover.Proof
proof, err := zk.ProtobufZKProofToProverProof(proofZkSNARK)
if err != nil {
return nil, fmt.Errorf("failed on zk.ProtobufZKProofToCircomProof: %w", err)
}
return proof, nil
}

// initializeZkVote initializes a zkSNARK vote. It does not check the proof nor includes the weight of the vote.
func initializeZkVote(voteEnvelope *models.VoteEnvelope, height uint32) *vstate.Vote {
func initializeZkVote(voteEnvelope *models.VoteEnvelope, height uint32) (*vstate.Vote, error) {
proof, err := zkProofFromEnvelope(voteEnvelope)
if err != nil {
return nil, err
}
nullifier, err := proof.Nullifier()
if err != nil {
return nil, fmt.Errorf("failed on parsing nullifier from public inputs: %w", err)
}

return &vstate.Vote{
Height: height,
ProcessID: voteEnvelope.ProcessId,
VotePackage: voteEnvelope.VotePackage,
Nullifier: voteEnvelope.Nullifier,
Nullifier: nullifier,
EncryptionKeyIndexes: voteEnvelope.EncryptionKeyIndexes,
}
}, nil
}

// initializeSignedVote initializes a signed vote. It does not check the proof nor includes the weight of the vote.
Expand Down

0 comments on commit bc5610a

Please sign in to comment.