├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── main.go ├── restapi ├── controller │ ├── handler.go │ ├── identity │ │ └── identity_controller.go │ └── subject │ │ └── subject_controller.go ├── model │ ├── identity │ │ └── identity_model.go │ └── subject │ │ └── subject_model.go ├── restapi.go └── server.go ├── snark ├── snark_data.zip └── verification_key.json └── zkvote ├── common ├── crypto │ ├── hash_wrapper.go │ └── mimc_wrapper.go ├── store │ ├── cache.go │ ├── node_validator.go │ └── store.go └── utils │ ├── constants.go │ ├── logger.go │ └── type_converter.go ├── model ├── ballot │ └── ballot.go ├── context │ └── context.go ├── identity │ ├── identity.go │ ├── merkle_tree.go │ └── merkle_tree_test.go ├── pb │ ├── zkvote.pb.go │ └── zkvote.proto └── subject │ └── subject.go ├── node ├── node.go └── zkp_vote │ ├── votes.go │ └── zkp_vote.go ├── operator ├── operator.go └── service │ ├── manager │ ├── manager.go │ ├── manager_db.go │ ├── manager_pubsub.go │ ├── protocol │ │ ├── ballot_protocol.go │ │ ├── factory.go │ │ ├── identity_protocol.go │ │ ├── network.go │ │ ├── protocol.go │ │ └── subject_protocol.go │ └── voter │ │ ├── identity_pool.go │ │ ├── identity_pool_test.go │ │ ├── proposal.go │ │ └── voter.go │ └── test │ ├── snark_verifier_test.go │ └── vectors │ ├── vote0.proof │ ├── vote1.proof │ ├── vote2.proof │ ├── vote3.proof │ ├── vote4.proof │ ├── vote5.proof │ ├── vote6.proof │ ├── vote7.proof │ ├── vote8.proof │ └── vote9.proof └── snark ├── verifier.go └── verifier_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | zkvote-node 15 | data/ 16 | logs.log 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Juin Chiu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zkvote: Using ZK-SNARK to Implement Decentralized Anonymous Voting on p2p Network 2 | **zkvote** is a powerful tool for anonymous voting. It uses a cryptographic function called a ZK-SNARK to keep the voter from revealing its identity. It is also built on a peer-to-peer network so that no single entity or authortity can control the access or result of the voting. Moreover, zkvote utilizes a developing standard called Decentralized Identifier (DID) and Verifiable Credential (VC) to prove the validity of the identity. 3 | 4 | ## How it Works? 5 | ![](https://i.imgur.com/RAAnWn8.png) 6 | 7 | 1. **Generate keypair** 8 | - Use `zkvote-web` to generate keypair / DID / identity commitment 9 | - Doesn't support import at this moment 10 | - Store in browser localStorage 11 | 2. **Propose/Join** 12 | - Proposer 13 | - Use `zkvote-web` to propose a new subject to be voted on 14 | - Joiner 15 | - Use `zkvote-web` to join an existing subject 16 | 3. **Exchange VC** 17 | - Joiner sends its identity commitment to proposer via other network 18 | - For example: email, SMS... 19 | - Proposer receives the identity commitment of the joiner from the network 20 | - Proposer generates a verifiable credential (VC) for joiner and sends it to joiner. This VC includes: 21 | - Subject hash 22 | - identity commitment of joiner 23 | - Signature of issuer 24 | - public key of issuer 25 | - Joiner receives the VC 26 | 4. **Vote** 27 | - Proposer 28 | - Generates the proof that corresponds to the subject 29 | - Uploads the proof and the VC to `zkvote-node` 30 | - Joiner 31 | - **Verify the VC for the subject** 32 | - Generates the proof that corresponds to the subject 33 | - Uploads the proof and the VC to `zkvote-node` 34 | 5. **Open** 35 | - Use `zkvote-web` to see the latest result 36 | 37 | ## Contribution 38 | See [this document](https://hackmd.io/@juincc/B1QV5NN5S) for more technical details 39 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/unitychain/zkvote-node 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect 7 | github.com/arnaucube/go-snark v0.0.4 8 | github.com/cbergoon/merkletree v0.2.0 9 | github.com/ethereum/go-ethereum v1.8.27 10 | github.com/gogo/protobuf v1.3.1 11 | github.com/golang/protobuf v1.3.2 12 | github.com/google/uuid v1.1.1 13 | github.com/gorilla/mux v1.7.3 14 | github.com/iden3/go-iden3-crypto v0.0.2 15 | github.com/ipfs/go-datastore v0.1.1 16 | github.com/ipfs/go-ds-leveldb v0.1.0 17 | github.com/ipfs/go-ipns v0.0.1 18 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect 19 | github.com/libp2p/go-libp2p v0.4.1 20 | github.com/libp2p/go-libp2p-circuit v0.1.4 21 | github.com/libp2p/go-libp2p-connmgr v0.1.1 22 | github.com/libp2p/go-libp2p-core v0.2.4 23 | github.com/libp2p/go-libp2p-discovery v0.2.0 24 | github.com/libp2p/go-libp2p-examples v0.1.0 // indirect 25 | github.com/libp2p/go-libp2p-kad-dht v0.3.0 26 | github.com/libp2p/go-libp2p-pubsub v0.2.1 27 | github.com/libp2p/go-libp2p-record v0.1.1 28 | github.com/manifoldco/promptui v0.3.2 29 | github.com/multiformats/go-multiaddr v0.1.1 30 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 31 | github.com/rs/cors v1.7.0 32 | github.com/sirupsen/logrus v1.4.2 // indirect 33 | github.com/stretchr/testify v1.4.0 34 | github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc 35 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 36 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 // indirect 37 | gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "strconv" 8 | 9 | levelds "github.com/ipfs/go-ds-leveldb" 10 | "github.com/unitychain/zkvote-node/restapi" 11 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 12 | "github.com/unitychain/zkvote-node/zkvote/node" 13 | zkvote "github.com/unitychain/zkvote-node/zkvote/operator" 14 | ) 15 | 16 | func main() { 17 | path := flag.String("db", "node_data", "Database folder") 18 | serverPort := flag.Int("p", 9900, "Web UI port") 19 | cmds := flag.Bool("cmds", false, "Interactive commands") 20 | type_operator := flag.Bool("op", true, "activate as an operator") 21 | type_node := flag.Bool("n", false, "activate as a node") 22 | flag.Parse() 23 | 24 | utils.OpenLog() 25 | utils.LogInfo("======================") 26 | utils.LogInfo("===== Node Start =====") 27 | utils.LogInfo("======================") 28 | 29 | // ~~ 0c. Note that contexts are an ugly way of controlling component 30 | // lifecycles. Talk about the service-based host refactor. 31 | ctx, cancel := context.WithCancel(context.Background()) 32 | defer cancel() 33 | 34 | // Set default values 35 | if *path == "" { 36 | *path = "node_data" 37 | } 38 | *path = "data/" + *path 39 | 40 | relay := false 41 | bucketSize := 1 42 | ds, err := levelds.NewDatastore(*path, nil) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | if *type_node { 48 | n := node.NewNode(ctx, ds, bucketSize) 49 | _ = n 50 | } else if *type_operator { 51 | serverAddr := ":" + strconv.Itoa(*serverPort) 52 | 53 | op, err := zkvote.NewOperator(ctx, ds, relay, bucketSize) 54 | if err != nil { 55 | panic(err) 56 | } 57 | 58 | server, err := restapi.NewServer(op, serverAddr) 59 | if err != nil { 60 | panic(err) 61 | } 62 | 63 | go server.ListenAndServe() 64 | fmt.Printf("HTTP server listens to port %d\n", *serverPort) 65 | 66 | if *cmds { 67 | op.Run() 68 | } else { 69 | select {} 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /restapi/controller/handler.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import "net/http" 4 | 5 | // Handler http handler for each controller API endpoint 6 | type Handler interface { 7 | Path() string 8 | Method() string 9 | Handle() http.HandlerFunc 10 | } 11 | 12 | // NewHTTPHandler returns instance of HTTPHandler which can be used handle 13 | // http requests 14 | func NewHTTPHandler(path, method string, handle http.HandlerFunc) *HTTPHandler { 15 | return &HTTPHandler{path: path, method: method, handle: handle} 16 | } 17 | 18 | // HTTPHandler contains REST API handling details which can be used to build routers 19 | // for http requests for given path 20 | type HTTPHandler struct { 21 | path string 22 | method string 23 | handle http.HandlerFunc 24 | } 25 | 26 | // Path returns http request path 27 | func (h *HTTPHandler) Path() string { 28 | return h.path 29 | } 30 | 31 | // Method returns http request method type 32 | func (h *HTTPHandler) Method() string { 33 | return h.method 34 | } 35 | 36 | // Handle returns http request handle func 37 | func (h *HTTPHandler) Handle() http.HandlerFunc { 38 | return h.handle 39 | } 40 | -------------------------------------------------------------------------------- /restapi/controller/identity/identity_controller.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | "os" 10 | "strconv" 11 | 12 | "github.com/unitychain/zkvote-node/restapi/controller" 13 | identityModel "github.com/unitychain/zkvote-node/restapi/model/identity" 14 | zkvote "github.com/unitychain/zkvote-node/zkvote/operator" 15 | // "errors" 16 | ) 17 | 18 | // var logger = log.New("aries-framework/did-exchange") 19 | 20 | const ( 21 | operationID = "/identities" 22 | getSnarkDataURL = operationID + "/snark_data" 23 | // acceptInvitationPath = operationID + "/{id}/accept-invitation" 24 | // connectionsByID = operationID + "/{id}" 25 | // acceptExchangeRequest = operationID + "/{id}/accept-request" 26 | // removeConnection = operationID + "/{id}/remove" 27 | // connectionsWebhookTopic = "connections" 28 | ) 29 | 30 | // Controller ... 31 | type Controller struct { 32 | handlers []controller.Handler 33 | *zkvote.Operator 34 | } 35 | 36 | // New ... 37 | func New(op *zkvote.Operator) (*Controller, error) { 38 | controller := &Controller{ 39 | Operator: op, 40 | } 41 | controller.registerHandler() 42 | 43 | // err = svc.startClientEventListener() 44 | // if err != nil { 45 | // return nil, fmt.Errorf("event listener startup failed: %w", err) 46 | // } 47 | 48 | return controller, nil 49 | } 50 | 51 | func (c *Controller) getSnarkData(rw http.ResponseWriter, req *http.Request) { 52 | // logger.Debugf("Querying subjects") 53 | 54 | var request identityModel.GetSnarkDataRequest 55 | 56 | err := getQueryParams(&request, req.URL.Query()) 57 | if err != nil { 58 | c.writeGenericError(rw, err, http.StatusInternalServerError) 59 | return 60 | } 61 | 62 | Filename := "snark/snark_data.zip" 63 | //Check if file exists and open 64 | Openfile, err := os.Open(Filename) 65 | defer Openfile.Close() //Close after function return 66 | if err != nil { 67 | //File not found, send 404 68 | http.Error(rw, "File not found.", 404) 69 | return 70 | } 71 | 72 | //File is found, create and send the correct headers 73 | 74 | //Get the Content-Type of the file 75 | //Create a buffer to store the header of the file in 76 | FileHeader := make([]byte, 512) 77 | //Copy the headers into the FileHeader buffer 78 | Openfile.Read(FileHeader) 79 | //Get content type of file 80 | FileContentType := http.DetectContentType(FileHeader) 81 | 82 | //Get the file size 83 | FileStat, _ := Openfile.Stat() //Get info from file 84 | FileSize := strconv.FormatInt(FileStat.Size(), 10) //Get file size as a string 85 | 86 | //Send the headers 87 | rw.Header().Set("Content-Disposition", "attachment; filename="+Filename) 88 | rw.Header().Set("Content-Type", FileContentType) 89 | rw.Header().Set("Content-Length", FileSize) 90 | 91 | //Send the file 92 | //We read 512 bytes from the file already, so we reset the offset back to 0 93 | Openfile.Seek(0, 0) 94 | io.Copy(rw, Openfile) //'Copy' the file to the client 95 | 96 | response := identityModel.GetSnarkDataResponse{ 97 | Results: "", 98 | } 99 | 100 | c.writeResponse(rw, response) 101 | } 102 | 103 | // writeGenericError writes given error to writer as generic error response 104 | func (c *Controller) writeGenericError(rw http.ResponseWriter, err error, statusCode int) { 105 | rw.WriteHeader(statusCode) 106 | rw.Header().Set("Content-Type", "application/json") 107 | 108 | json.NewEncoder(rw).Encode(identityModel.GenericError{ 109 | Body: struct { 110 | Code int32 `json:"code"` 111 | Message string `json:"message"` 112 | }{ 113 | // TODO implement error codes, below is sample error code 114 | Code: 1, 115 | Message: err.Error(), 116 | }, 117 | }) 118 | } 119 | 120 | // writeResponse writes interface value to response 121 | func (c *Controller) writeResponse(rw io.Writer, v interface{}) { 122 | err := json.NewEncoder(rw).Encode(v) 123 | // as of now, just log errors for writing response 124 | if err != nil { 125 | // logger.Errorf("Unable to send error response, %s", err) 126 | fmt.Printf("Unable to send error response, %s\n", err) 127 | } 128 | } 129 | 130 | // GetRESTHandlers get all controller API handler available for this protocol service 131 | func (c *Controller) GetRESTHandlers() []controller.Handler { 132 | return c.handlers 133 | } 134 | 135 | // registerHandler register handlers to be exposed from this protocol service as REST API endpoints 136 | func (c *Controller) registerHandler() { 137 | // Add more protocol endpoints here to expose them as controller API endpoints 138 | c.handlers = []controller.Handler{ 139 | controller.NewHTTPHandler(getSnarkDataURL, http.MethodGet, c.getSnarkData), 140 | // support.NewHTTPHandler(connections, http.MethodGet, c.QueryConnections), 141 | // support.NewHTTPHandler(connectionsByID, http.MethodGet, c.QueryConnectionByID), 142 | // support.NewHTTPHandler(receiveInvitationPath, http.MethodPost, c.ReceiveInvitation), 143 | // support.NewHTTPHandler(acceptInvitationPath, http.MethodPost, c.AcceptInvitation), 144 | // support.NewHTTPHandler(acceptExchangeRequest, http.MethodPost, c.AcceptExchangeRequest), 145 | // support.NewHTTPHandler(removeConnection, http.MethodPost, c.RemoveConnection), 146 | } 147 | } 148 | 149 | // getQueryParams converts query strings to `map[string]string` 150 | // and unmarshals to the value pointed by v by following 151 | // `json.Unmarshal` rules. 152 | func getQueryParams(v interface{}, vals url.Values) error { 153 | // normalize all query string key/values 154 | args := make(map[string]string) 155 | 156 | for k, v := range vals { 157 | if len(v) > 0 { 158 | args[k] = v[0] 159 | } 160 | } 161 | 162 | bytes, err := json.Marshal(args) 163 | if err != nil { 164 | return err 165 | } 166 | 167 | return json.Unmarshal(bytes, v) 168 | } 169 | 170 | // // CreateInvitation swagger:route POST /connections/create-invitation did-exchange createInvitation 171 | // // 172 | // // Creates a new connection invitation.... 173 | // // 174 | // // Responses: 175 | // // default: genericError 176 | // // 200: createInvitationResponse 177 | // func (c *Operation) CreateInvitation(rw http.ResponseWriter, req *http.Request) { 178 | // logger.Debugf("Creating connection invitation ") 179 | 180 | // var request models.CreateInvitationRequest 181 | 182 | // err := getQueryParams(&request, req.URL.Query()) 183 | // if err != nil { 184 | // c.writeGenericError(rw, err) 185 | // return 186 | // } 187 | 188 | // var alias, did string 189 | // if request.CreateInvitationParams != nil { 190 | // alias = request.CreateInvitationParams.Alias 191 | // did = request.CreateInvitationParams.Public 192 | // } 193 | 194 | // var invitation *didexchange.Invitation 195 | // // call didexchange client 196 | // if did != "" { 197 | // invitation, err = c.client.CreateInvitationWithDID(c.defaultLabel, did) 198 | // } else { 199 | // invitation, err = c.client.CreateInvitation(c.defaultLabel) 200 | // } 201 | 202 | // if err != nil { 203 | // c.writeGenericError(rw, err) 204 | // return 205 | // } 206 | 207 | // c.writeResponse(rw, &models.CreateInvitationResponse{ 208 | // Invitation: invitation, 209 | // Alias: alias}) 210 | // } 211 | 212 | // // ReceiveInvitation swagger:route POST /connections/receive-invitation did-exchange receiveInvitation 213 | // // 214 | // // Receive a new connection invitation.... 215 | // // 216 | // // Responses: 217 | // // default: genericError 218 | // // 200: receiveInvitationResponse 219 | // func (c *Operation) ReceiveInvitation(rw http.ResponseWriter, req *http.Request) { 220 | // logger.Debugf("Receiving connection invitation ") 221 | 222 | // var request models.ReceiveInvitationRequest 223 | 224 | // err := json.NewDecoder(req.Body).Decode(&request.Invitation) 225 | // if err != nil { 226 | // c.writeGenericError(rw, err) 227 | // return 228 | // } 229 | 230 | // connectionID, err := c.client.HandleInvitation(request.Invitation) 231 | // if err != nil { 232 | // c.writeGenericError(rw, err) 233 | // return 234 | // } 235 | 236 | // resp := models.ReceiveInvitationResponse{ 237 | // ConnectionID: connectionID, 238 | // } 239 | 240 | // c.writeResponse(rw, resp) 241 | // } 242 | 243 | // // AcceptInvitation swagger:route POST /connections/{id}/accept-invitation did-exchange acceptInvitation 244 | // // 245 | // // Accept a stored connection invitation.... 246 | // // 247 | // // Responses: 248 | // // default: genericError 249 | // // 200: acceptInvitationResponse 250 | // func (c *Operation) AcceptInvitation(rw http.ResponseWriter, req *http.Request) { 251 | // params := mux.Vars(req) 252 | // logger.Debugf("Accepting connection invitation for id[%s]", params["id"]) 253 | 254 | // err := c.client.AcceptInvitation(params["id"]) 255 | // if err != nil { 256 | // logger.Errorf("accept invitation api failed for id %s with error %s", params["id"], err) 257 | // c.writeGenericError(rw, err) 258 | 259 | // return 260 | // } 261 | 262 | // response := &models.AcceptInvitationResponse{ 263 | // ConnectionID: params["id"], 264 | // } 265 | 266 | // c.writeResponse(rw, response) 267 | // } 268 | 269 | // // AcceptExchangeRequest swagger:route POST /connections/{id}/accept-request did-exchange acceptRequest 270 | // // 271 | // // Accepts a stored connection request. 272 | // // 273 | // // Responses: 274 | // // default: genericError 275 | // // 200: acceptExchangeResponse 276 | // func (c *Operation) AcceptExchangeRequest(rw http.ResponseWriter, req *http.Request) { 277 | // params := mux.Vars(req) 278 | // logger.Infof("Accepting connection request for id [%s]", params["id"]) 279 | 280 | // err := c.client.AcceptExchangeRequest(params["id"]) 281 | // if err != nil { 282 | // logger.Errorf("accepting connection request failed for id %s with error %s", params["id"], err) 283 | // c.writeGenericError(rw, err) 284 | 285 | // return 286 | // } 287 | 288 | // result := &models.ExchangeResponse{ 289 | // ConnectionID: params["id"], 290 | // } 291 | 292 | // response := models.AcceptExchangeResult{Result: result} 293 | 294 | // c.writeResponse(rw, response) 295 | // } 296 | 297 | // QuerySubjects swagger:route GET /connections did-exchange queryConnections 298 | // 299 | // query agent to agent connections. 300 | // 301 | // Responses: 302 | // default: genericError 303 | // 200: queryConnectionsResponse 304 | 305 | // // QueryConnectionByID swagger:route GET /connections/{id} did-exchange getConnection 306 | // // 307 | // // Fetch a single connection record. 308 | // // 309 | // // Responses: 310 | // // default: genericError 311 | // // 200: queryConnectionResponse 312 | // func (c *Operation) QueryConnectionByID(rw http.ResponseWriter, req *http.Request) { 313 | // params := mux.Vars(req) 314 | // logger.Debugf("Querying connection invitation for id [%s]", params["id"]) 315 | 316 | // result, err := c.client.GetConnection(params["id"]) 317 | // if err != nil { 318 | // c.writeGenericError(rw, err) 319 | // return 320 | // } 321 | 322 | // response := models.QueryConnectionResponse{ 323 | // Result: result, 324 | // } 325 | 326 | // c.writeResponse(rw, response) 327 | // } 328 | 329 | // // RemoveConnection swagger:route POST /connections/{id}/remove did-exchange removeConnection 330 | // // 331 | // // Removes given connection record. 332 | // // 333 | // // Responses: 334 | // // default: genericError 335 | // // 200: removeConnectionResponse 336 | // func (c *Operation) RemoveConnection(rw http.ResponseWriter, req *http.Request) { 337 | // params := mux.Vars(req) 338 | // logger.Debugf("Removing connection record for id [%s]", params["id"]) 339 | 340 | // err := c.client.RemoveConnection(params["id"]) 341 | // if err != nil { 342 | // c.writeGenericError(rw, err) 343 | // return 344 | // } 345 | // } 346 | 347 | // // startClientEventListener listens to action and message events from DID Exchange service. 348 | // func (c *Operation) startClientEventListener() error { 349 | // // register the message event channel 350 | // err := c.client.RegisterMsgEvent(c.msgCh) 351 | // if err != nil { 352 | // return fmt.Errorf("didexchange message event registration failed: %w", err) 353 | // } 354 | 355 | // // event listeners 356 | // go func() { 357 | // for e := range c.msgCh { 358 | // err := c.handleMessageEvents(e) 359 | // if err != nil { 360 | // logger.Errorf("handle message events failed : %s", err) 361 | // } 362 | // } 363 | // }() 364 | 365 | // return nil 366 | // } 367 | 368 | // func (c *Operation) handleMessageEvents(e service.StateMsg) error { 369 | // if e.Type == service.PostState { 370 | // switch v := e.Properties.(type) { 371 | // case didexchange.Event: 372 | // props := v 373 | 374 | // err := c.sendConnectionNotification(props.ConnectionID(), e.StateID) 375 | // if err != nil { 376 | // return fmt.Errorf("send connection notification failed : %w", err) 377 | // } 378 | // case error: 379 | // return fmt.Errorf("service processing failed : %w", v) 380 | // default: 381 | // return errors.New("event is not of DIDExchange event type") 382 | // } 383 | // } 384 | 385 | // return nil 386 | // } 387 | 388 | // func (c *Operation) sendConnectionNotification(connectionID, stateID string) error { 389 | // conn, err := c.client.GetConnectionAtState(connectionID, stateID) 390 | // if err != nil { 391 | // logger.Errorf("Send notification failed, topic[%s], connectionID[%s]", connectionsWebhookTopic, connectionID) 392 | // return fmt.Errorf("connection notification webhook : %w", err) 393 | // } 394 | 395 | // connMsg := &ConnectionMsg{ 396 | // ConnectionID: conn.ConnectionID, 397 | // State: conn.State, 398 | // MyDid: conn.MyDID, 399 | // TheirDid: conn.TheirDID, 400 | // TheirLabel: conn.TheirLabel, 401 | // TheirRole: conn.TheirLabel, 402 | // } 403 | 404 | // jsonMessage, err := json.Marshal(connMsg) 405 | // if err != nil { 406 | // return fmt.Errorf("connection notification json marshal : %w", err) 407 | // } 408 | 409 | // logger.Debugf("Sending notification on topic '%s', message body : %s", connectionsWebhookTopic, jsonMessage) 410 | 411 | // err = c.notifier.Notify(connectionsWebhookTopic, jsonMessage) 412 | // if err != nil { 413 | // return fmt.Errorf("connection notification webhook : %w", err) 414 | // } 415 | 416 | // return nil 417 | // } 418 | -------------------------------------------------------------------------------- /restapi/controller/subject/subject_controller.go: -------------------------------------------------------------------------------- 1 | package subject 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | 10 | "github.com/unitychain/zkvote-node/restapi/controller" 11 | subjectModel "github.com/unitychain/zkvote-node/restapi/model/subject" 12 | subject "github.com/unitychain/zkvote-node/zkvote/model/subject" 13 | zkvote "github.com/unitychain/zkvote-node/zkvote/operator" 14 | // "errors" 15 | ) 16 | 17 | // var logger = log.New("aries-framework/did-exchange") 18 | 19 | const ( 20 | operationID = "/subjects" 21 | indexURL = operationID 22 | proposeURL = operationID + "/propose" 23 | joinURL = operationID + "/join" 24 | voteURL = operationID + "/vote" 25 | openURL = operationID + "/open" 26 | getIdentityPathURL = operationID + "/identity_path" 27 | // receiveInvitationPath = operationID + "/receive-invitation" 28 | // acceptInvitationPath = operationID + "/{id}/accept-invitation" 29 | // connectionsByID = operationID + "/{id}" 30 | // acceptExchangeRequest = operationID + "/{id}/accept-request" 31 | // removeConnection = operationID + "/{id}/remove" 32 | // connectionsWebhookTopic = "connections" 33 | ) 34 | 35 | // Controller ... 36 | type Controller struct { 37 | handlers []controller.Handler 38 | *zkvote.Operator 39 | } 40 | 41 | // New ... 42 | func New(op *zkvote.Operator) (*Controller, error) { 43 | controller := &Controller{ 44 | Operator: op, 45 | } 46 | controller.registerHandler() 47 | 48 | // err = svc.startClientEventListener() 49 | // if err != nil { 50 | // return nil, fmt.Errorf("event listener startup failed: %w", err) 51 | // } 52 | 53 | return controller, nil 54 | } 55 | 56 | func (c *Controller) index(rw http.ResponseWriter, req *http.Request) { 57 | // logger.Debugf("Querying subjects") 58 | 59 | var request subjectModel.IndexRequest 60 | 61 | err := getQueryParams(&request, req.URL.Query()) 62 | if err != nil { 63 | c.writeGenericError(rw, err, http.StatusInternalServerError) 64 | return 65 | } 66 | 67 | subjects, err := c.Manager.GetSubjectList() 68 | if err != nil { 69 | c.writeGenericError(rw, err, http.StatusInternalServerError) 70 | return 71 | } 72 | 73 | results := subjectToJSON(subjects) 74 | 75 | response := subjectModel.IndexResponse{ 76 | Results: results, 77 | } 78 | 79 | c.writeResponse(rw, response) 80 | } 81 | 82 | func (c *Controller) propose(rw http.ResponseWriter, req *http.Request) { 83 | // logger.Debugf("Querying subjects") 84 | 85 | var request subjectModel.ProposeRequest 86 | 87 | err := req.ParseMultipartForm(0) 88 | if err != nil { 89 | c.writeGenericError(rw, err, http.StatusInternalServerError) 90 | return 91 | } 92 | 93 | err = getQueryParams(&request, req.Form) 94 | if err != nil { 95 | c.writeGenericError(rw, err, http.StatusInternalServerError) 96 | return 97 | } 98 | 99 | var title, description, identityCommitment string 100 | if request.ProposeParams != nil { 101 | title = request.ProposeParams.Title 102 | description = request.ProposeParams.Description 103 | identityCommitment = request.ProposeParams.IdentityCommitment 104 | err := c.Propose(title, description, identityCommitment) 105 | if err != nil { 106 | c.writeGenericError(rw, err, http.StatusInternalServerError) 107 | return 108 | } 109 | } 110 | 111 | response := subjectModel.ProposeResponse{ 112 | Results: "Success", 113 | } 114 | 115 | c.writeResponse(rw, response) 116 | } 117 | 118 | func (c *Controller) join(rw http.ResponseWriter, req *http.Request) { 119 | // logger.Debugf("Querying subjects") 120 | 121 | var request subjectModel.JoinRequest 122 | 123 | err := req.ParseMultipartForm(0) 124 | if err != nil { 125 | c.writeGenericError(rw, err, http.StatusInternalServerError) 126 | return 127 | } 128 | 129 | err = getQueryParams(&request, req.Form) 130 | if err != nil { 131 | c.writeGenericError(rw, err, http.StatusInternalServerError) 132 | return 133 | } 134 | 135 | var subjectHash, identityCommitment string 136 | if request.JoinParams != nil { 137 | subjectHash = request.JoinParams.SubjectHash 138 | identityCommitment = request.JoinParams.IdentityCommitment 139 | err = c.Join(subjectHash, identityCommitment) 140 | } 141 | if err != nil { 142 | c.writeGenericError(rw, err, http.StatusInternalServerError) 143 | return 144 | } 145 | 146 | response := subjectModel.JoinResponse{ 147 | Results: "Success", 148 | } 149 | 150 | c.writeResponse(rw, response) 151 | } 152 | 153 | func (c *Controller) vote(rw http.ResponseWriter, req *http.Request) { 154 | // logger.Debugf("Querying subjects") 155 | 156 | var request subjectModel.VoteRequest 157 | 158 | err := req.ParseMultipartForm(0) 159 | if err != nil { 160 | c.writeGenericError(rw, err, http.StatusInternalServerError) 161 | return 162 | } 163 | 164 | err = getQueryParams(&request, req.Form) 165 | if err != nil { 166 | c.writeGenericError(rw, err, http.StatusInternalServerError) 167 | return 168 | } 169 | 170 | if request.VoteParams != nil { 171 | subjectHash := request.VoteParams.SubjectHash 172 | proof := request.VoteParams.Proof 173 | err = c.Vote(subjectHash, proof) 174 | } 175 | if err != nil { 176 | c.writeGenericError(rw, err, http.StatusInternalServerError) 177 | return 178 | } 179 | 180 | response := subjectModel.VoteResponse{ 181 | Results: "Success", 182 | } 183 | 184 | c.writeResponse(rw, response) 185 | } 186 | 187 | func (c *Controller) open(rw http.ResponseWriter, req *http.Request) { 188 | // logger.Debugf("Querying subjects") 189 | 190 | var request subjectModel.OpenRequest 191 | 192 | err := getQueryParams(&request, req.URL.Query()) 193 | 194 | if err != nil { 195 | c.writeGenericError(rw, err, http.StatusInternalServerError) 196 | return 197 | } 198 | 199 | response := subjectModel.OpenResponse{} 200 | if request.OpenParams != nil { 201 | subjectHash := request.OpenParams.SubjectHash 202 | yes, no := c.Open(subjectHash) 203 | response.Results.Yes = yes 204 | response.Results.No = no 205 | } 206 | 207 | if err != nil { 208 | c.writeGenericError(rw, err, http.StatusInternalServerError) 209 | return 210 | } 211 | 212 | c.writeResponse(rw, response) 213 | } 214 | 215 | func (c *Controller) getIdentityPath(rw http.ResponseWriter, req *http.Request) { 216 | // logger.Debugf("Querying subjects") 217 | 218 | var request subjectModel.GetIdentityPathRequest 219 | 220 | err := getQueryParams(&request, req.URL.Query()) 221 | if err != nil { 222 | c.writeGenericError(rw, err, http.StatusInternalServerError) 223 | return 224 | } 225 | 226 | response := subjectModel.GetIdentityPathResponse{} 227 | if request.GetIdentityPathParams != nil { 228 | subjectHash := request.GetIdentityPathParams.SubjectHash 229 | identityCommitment := request.GetIdentityPathParams.IdentityCommitment 230 | path, index, root, err := c.Manager.GetIdentityPath(subjectHash, identityCommitment) 231 | if err != nil { 232 | c.writeGenericError(rw, err, http.StatusInternalServerError) 233 | return 234 | } 235 | 236 | response.Results.Path = path 237 | response.Results.Index = index 238 | response.Results.Root = root 239 | } 240 | 241 | c.writeResponse(rw, response) 242 | } 243 | 244 | func subjectToJSON(s []*subject.Subject) []map[string]string { 245 | result := make([]map[string]string, 0) 246 | for _, s := range s { 247 | result = append(result, s.JSON()) 248 | } 249 | return result 250 | } 251 | 252 | // writeGenericError writes given error to writer as generic error response 253 | func (c *Controller) writeGenericError(rw http.ResponseWriter, err error, statusCode int) { 254 | rw.WriteHeader(statusCode) 255 | rw.Header().Set("Content-Type", "application/json") 256 | 257 | json.NewEncoder(rw).Encode(subjectModel.GenericError{ 258 | Body: struct { 259 | Code int32 `json:"code"` 260 | Message string `json:"message"` 261 | }{ 262 | // TODO implement error codes, below is sample error code 263 | Code: 1, 264 | Message: err.Error(), 265 | }, 266 | }) 267 | } 268 | 269 | // writeResponse writes interface value to response 270 | func (c *Controller) writeResponse(rw io.Writer, v interface{}) { 271 | err := json.NewEncoder(rw).Encode(v) 272 | // as of now, just log errors for writing response 273 | if err != nil { 274 | // logger.Errorf("Unable to send error response, %s", err) 275 | fmt.Printf("Unable to send error response, %s\n", err) 276 | } 277 | } 278 | 279 | // GetRESTHandlers get all controller API handler available for this protocol service 280 | func (c *Controller) GetRESTHandlers() []controller.Handler { 281 | return c.handlers 282 | } 283 | 284 | // registerHandler register handlers to be exposed from this protocol service as REST API endpoints 285 | func (c *Controller) registerHandler() { 286 | // Add more protocol endpoints here to expose them as controller API endpoints 287 | c.handlers = []controller.Handler{ 288 | controller.NewHTTPHandler(indexURL, http.MethodGet, c.index), 289 | controller.NewHTTPHandler(proposeURL, http.MethodPost, c.propose), 290 | controller.NewHTTPHandler(joinURL, http.MethodPost, c.join), 291 | controller.NewHTTPHandler(voteURL, http.MethodPost, c.vote), 292 | controller.NewHTTPHandler(openURL, http.MethodGet, c.open), 293 | controller.NewHTTPHandler(getIdentityPathURL, http.MethodGet, c.getIdentityPath), 294 | // support.NewHTTPHandler(connections, http.MethodGet, c.QueryConnections), 295 | // support.NewHTTPHandler(connectionsByID, http.MethodGet, c.QueryConnectionByID), 296 | // support.NewHTTPHandler(acceptInvitationPath, http.MethodPost, c.AcceptInvitation), 297 | // support.NewHTTPHandler(acceptExchangeRequest, http.MethodPost, c.AcceptExchangeRequest), 298 | // support.NewHTTPHandler(removeConnection, http.MethodPost, c.RemoveConnection), 299 | } 300 | } 301 | 302 | // getQueryParams converts query strings to `map[string]string` 303 | // and unmarshals to the value pointed by v by following 304 | // `json.Unmarshal` rules. 305 | func getQueryParams(v interface{}, vals url.Values) error { 306 | // normalize all query string key/values 307 | args := make(map[string]string) 308 | 309 | for k, v := range vals { 310 | if len(v) > 0 { 311 | args[k] = v[0] 312 | } 313 | } 314 | 315 | bytes, err := json.Marshal(args) 316 | if err != nil { 317 | return err 318 | } 319 | 320 | return json.Unmarshal(bytes, v) 321 | } 322 | 323 | // // CreateInvitation swagger:route POST /connections/create-invitation did-exchange createInvitation 324 | // // 325 | // // Creates a new connection invitation.... 326 | // // 327 | // // Responses: 328 | // // default: genericError 329 | // // 200: createInvitationResponse 330 | // func (c *Operation) CreateInvitation(rw http.ResponseWriter, req *http.Request) { 331 | // logger.Debugf("Creating connection invitation ") 332 | 333 | // var request models.CreateInvitationRequest 334 | 335 | // err := getQueryParams(&request, req.URL.Query()) 336 | // if err != nil { 337 | // c.writeGenericError(rw, err) 338 | // return 339 | // } 340 | 341 | // var alias, did string 342 | // if request.CreateInvitationParams != nil { 343 | // alias = request.CreateInvitationParams.Alias 344 | // did = request.CreateInvitationParams.Public 345 | // } 346 | 347 | // var invitation *didexchange.Invitation 348 | // // call didexchange client 349 | // if did != "" { 350 | // invitation, err = c.client.CreateInvitationWithDID(c.defaultLabel, did) 351 | // } else { 352 | // invitation, err = c.client.CreateInvitation(c.defaultLabel) 353 | // } 354 | 355 | // if err != nil { 356 | // c.writeGenericError(rw, err) 357 | // return 358 | // } 359 | 360 | // c.writeResponse(rw, &models.CreateInvitationResponse{ 361 | // Invitation: invitation, 362 | // Alias: alias}) 363 | // } 364 | 365 | // // ReceiveInvitation swagger:route POST /connections/receive-invitation did-exchange receiveInvitation 366 | // // 367 | // // Receive a new connection invitation.... 368 | // // 369 | // // Responses: 370 | // // default: genericError 371 | // // 200: receiveInvitationResponse 372 | // func (c *Operation) ReceiveInvitation(rw http.ResponseWriter, req *http.Request) { 373 | // logger.Debugf("Receiving connection invitation ") 374 | 375 | // var request models.ReceiveInvitationRequest 376 | 377 | // err := json.NewDecoder(req.Body).Decode(&request.Invitation) 378 | // if err != nil { 379 | // c.writeGenericError(rw, err) 380 | // return 381 | // } 382 | 383 | // connectionID, err := c.client.HandleInvitation(request.Invitation) 384 | // if err != nil { 385 | // c.writeGenericError(rw, err) 386 | // return 387 | // } 388 | 389 | // resp := models.ReceiveInvitationResponse{ 390 | // ConnectionID: connectionID, 391 | // } 392 | 393 | // c.writeResponse(rw, resp) 394 | // } 395 | 396 | // // AcceptInvitation swagger:route POST /connections/{id}/accept-invitation did-exchange acceptInvitation 397 | // // 398 | // // Accept a stored connection invitation.... 399 | // // 400 | // // Responses: 401 | // // default: genericError 402 | // // 200: acceptInvitationResponse 403 | // func (c *Operation) AcceptInvitation(rw http.ResponseWriter, req *http.Request) { 404 | // params := mux.Vars(req) 405 | // logger.Debugf("Accepting connection invitation for id[%s]", params["id"]) 406 | 407 | // err := c.client.AcceptInvitation(params["id"]) 408 | // if err != nil { 409 | // logger.Errorf("accept invitation api failed for id %s with error %s", params["id"], err) 410 | // c.writeGenericError(rw, err) 411 | 412 | // return 413 | // } 414 | 415 | // response := &models.AcceptInvitationResponse{ 416 | // ConnectionID: params["id"], 417 | // } 418 | 419 | // c.writeResponse(rw, response) 420 | // } 421 | 422 | // // AcceptExchangeRequest swagger:route POST /connections/{id}/accept-request did-exchange acceptRequest 423 | // // 424 | // // Accepts a stored connection request. 425 | // // 426 | // // Responses: 427 | // // default: genericError 428 | // // 200: acceptExchangeResponse 429 | // func (c *Operation) AcceptExchangeRequest(rw http.ResponseWriter, req *http.Request) { 430 | // params := mux.Vars(req) 431 | // logger.Infof("Accepting connection request for id [%s]", params["id"]) 432 | 433 | // err := c.client.AcceptExchangeRequest(params["id"]) 434 | // if err != nil { 435 | // logger.Errorf("accepting connection request failed for id %s with error %s", params["id"], err) 436 | // c.writeGenericError(rw, err) 437 | 438 | // return 439 | // } 440 | 441 | // result := &models.ExchangeResponse{ 442 | // ConnectionID: params["id"], 443 | // } 444 | 445 | // response := models.AcceptExchangeResult{Result: result} 446 | 447 | // c.writeResponse(rw, response) 448 | // } 449 | 450 | // QuerySubjects swagger:route GET /connections did-exchange queryConnections 451 | // 452 | // query agent to agent connections. 453 | // 454 | // Responses: 455 | // default: genericError 456 | // 200: queryConnectionsResponse 457 | 458 | // // QueryConnectionByID swagger:route GET /connections/{id} did-exchange getConnection 459 | // // 460 | // // Fetch a single connection record. 461 | // // 462 | // // Responses: 463 | // // default: genericError 464 | // // 200: queryConnectionResponse 465 | // func (c *Operation) QueryConnectionByID(rw http.ResponseWriter, req *http.Request) { 466 | // params := mux.Vars(req) 467 | // logger.Debugf("Querying connection invitation for id [%s]", params["id"]) 468 | 469 | // result, err := c.client.GetConnection(params["id"]) 470 | // if err != nil { 471 | // c.writeGenericError(rw, err) 472 | // return 473 | // } 474 | 475 | // response := models.QueryConnectionResponse{ 476 | // Result: result, 477 | // } 478 | 479 | // c.writeResponse(rw, response) 480 | // } 481 | 482 | // // RemoveConnection swagger:route POST /connections/{id}/remove did-exchange removeConnection 483 | // // 484 | // // Removes given connection record. 485 | // // 486 | // // Responses: 487 | // // default: genericError 488 | // // 200: removeConnectionResponse 489 | // func (c *Operation) RemoveConnection(rw http.ResponseWriter, req *http.Request) { 490 | // params := mux.Vars(req) 491 | // logger.Debugf("Removing connection record for id [%s]", params["id"]) 492 | 493 | // err := c.client.RemoveConnection(params["id"]) 494 | // if err != nil { 495 | // c.writeGenericError(rw, err) 496 | // return 497 | // } 498 | // } 499 | 500 | // // startClientEventListener listens to action and message events from DID Exchange service. 501 | // func (c *Operation) startClientEventListener() error { 502 | // // register the message event channel 503 | // err := c.client.RegisterMsgEvent(c.msgCh) 504 | // if err != nil { 505 | // return fmt.Errorf("didexchange message event registration failed: %w", err) 506 | // } 507 | 508 | // // event listeners 509 | // go func() { 510 | // for e := range c.msgCh { 511 | // err := c.handleMessageEvents(e) 512 | // if err != nil { 513 | // logger.Errorf("handle message events failed : %s", err) 514 | // } 515 | // } 516 | // }() 517 | 518 | // return nil 519 | // } 520 | 521 | // func (c *Operation) handleMessageEvents(e service.StateMsg) error { 522 | // if e.Type == service.PostState { 523 | // switch v := e.Properties.(type) { 524 | // case didexchange.Event: 525 | // props := v 526 | 527 | // err := c.sendConnectionNotification(props.ConnectionID(), e.StateID) 528 | // if err != nil { 529 | // return fmt.Errorf("send connection notification failed : %w", err) 530 | // } 531 | // case error: 532 | // return fmt.Errorf("service processing failed : %w", v) 533 | // default: 534 | // return errors.New("event is not of DIDExchange event type") 535 | // } 536 | // } 537 | 538 | // return nil 539 | // } 540 | 541 | // func (c *Operation) sendConnectionNotification(connectionID, stateID string) error { 542 | // conn, err := c.client.GetConnectionAtState(connectionID, stateID) 543 | // if err != nil { 544 | // logger.Errorf("Send notification failed, topic[%s], connectionID[%s]", connectionsWebhookTopic, connectionID) 545 | // return fmt.Errorf("connection notification webhook : %w", err) 546 | // } 547 | 548 | // connMsg := &ConnectionMsg{ 549 | // ConnectionID: conn.ConnectionID, 550 | // State: conn.State, 551 | // MyDid: conn.MyDID, 552 | // TheirDid: conn.TheirDID, 553 | // TheirLabel: conn.TheirLabel, 554 | // TheirRole: conn.TheirLabel, 555 | // } 556 | 557 | // jsonMessage, err := json.Marshal(connMsg) 558 | // if err != nil { 559 | // return fmt.Errorf("connection notification json marshal : %w", err) 560 | // } 561 | 562 | // logger.Debugf("Sending notification on topic '%s', message body : %s", connectionsWebhookTopic, jsonMessage) 563 | 564 | // err = c.notifier.Notify(connectionsWebhookTopic, jsonMessage) 565 | // if err != nil { 566 | // return fmt.Errorf("connection notification webhook : %w", err) 567 | // } 568 | 569 | // return nil 570 | // } 571 | -------------------------------------------------------------------------------- /restapi/model/identity/identity_model.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | // A GenericError is the default error message that is generated. 4 | // For certain status codes there are more appropriate error structures. 5 | // 6 | // swagger:response genericError 7 | type GenericError struct { 8 | // in: body 9 | Body struct { 10 | Code int32 `json:"code"` 11 | Message string `json:"message"` 12 | } `json:"body"` 13 | } 14 | 15 | // GetSnarkDataRequest ... 16 | type GetSnarkDataRequest struct{} 17 | 18 | // GetSnarkDataResponse ... 19 | type GetSnarkDataResponse struct { 20 | // in: body 21 | Results string `json:"results"` 22 | } 23 | -------------------------------------------------------------------------------- /restapi/model/subject/subject_model.go: -------------------------------------------------------------------------------- 1 | package subject 2 | 3 | // A GenericError is the default error message that is generated. 4 | // For certain status codes there are more appropriate error structures. 5 | // 6 | // swagger:response genericError 7 | type GenericError struct { 8 | // in: body 9 | Body struct { 10 | Code int32 `json:"code"` 11 | Message string `json:"message"` 12 | } `json:"body"` 13 | } 14 | 15 | // IndexRequest ... 16 | type IndexRequest struct{} 17 | 18 | // ProposeRequest ... 19 | type ProposeRequest struct { 20 | *ProposeParams 21 | } 22 | 23 | // JoinRequest ... 24 | type JoinRequest struct { 25 | *JoinParams 26 | } 27 | 28 | // VoteRequest ... 29 | type VoteRequest struct { 30 | *VoteParams 31 | } 32 | 33 | // OpenRequest ... 34 | type OpenRequest struct { 35 | *OpenParams 36 | } 37 | 38 | // GetIdentityPathRequest ... 39 | type GetIdentityPathRequest struct { 40 | *GetIdentityPathParams 41 | } 42 | 43 | // ProposeParams ... 44 | type ProposeParams struct { 45 | Title string `json:"title"` 46 | Description string `json:"description"` 47 | IdentityCommitment string `json:"identityCommitment"` 48 | } 49 | 50 | // JoinParams ... 51 | type JoinParams struct { 52 | SubjectHash string `json:"subjectHash"` 53 | IdentityCommitment string `json:"identityCommitment"` 54 | } 55 | 56 | // VoteParams ... 57 | type VoteParams struct { 58 | SubjectHash string `json:"subjectHash"` 59 | Proof string `json:"proof"` 60 | } 61 | 62 | // OpenParams ... 63 | type OpenParams struct { 64 | SubjectHash string `json:"subjectHash"` 65 | } 66 | 67 | // GetIdentityPathParams ... 68 | type GetIdentityPathParams struct { 69 | SubjectHash string `json:"subjectHash"` 70 | IdentityCommitment string `json:"identityCommitment"` 71 | } 72 | 73 | // IndexResponse ... 74 | type IndexResponse struct { 75 | // in: body 76 | Results []map[string]string `json:"results"` 77 | } 78 | 79 | // ProposeResponse ... 80 | type ProposeResponse struct { 81 | // in: body 82 | Results string `json:"results"` 83 | } 84 | 85 | // JoinResponse ... 86 | type JoinResponse struct { 87 | // in: body 88 | Results string `json:"results"` 89 | } 90 | 91 | // VoteResponse ... 92 | type VoteResponse struct { 93 | // in: body 94 | Results string `json:"results"` 95 | } 96 | 97 | // OpenResponse ... 98 | type OpenResponse struct { 99 | // in: body 100 | Results struct { 101 | Yes int `json:"yes"` 102 | No int `json:"no"` 103 | } `json:"results"` 104 | } 105 | 106 | // GetIdentityPathResponse ... 107 | type GetIdentityPathResponse struct { 108 | // in: body 109 | Results struct { 110 | Path []string `json:"path"` 111 | Index []int `json:"index"` 112 | Root string `json:"root"` 113 | } `json:"results"` 114 | } 115 | -------------------------------------------------------------------------------- /restapi/restapi.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/unitychain/zkvote-node/restapi/controller" 7 | identityController "github.com/unitychain/zkvote-node/restapi/controller/identity" 8 | subjectController "github.com/unitychain/zkvote-node/restapi/controller/subject" 9 | zkvote "github.com/unitychain/zkvote-node/zkvote/operator" 10 | ) 11 | 12 | type allOpts struct { 13 | webhookURLs []string 14 | defaultLabel string 15 | } 16 | 17 | // Opt represents a REST Api option. 18 | type Opt func(opts *allOpts) 19 | 20 | // RESTAPI contains handlers for REST API 21 | type RESTAPI struct { 22 | handlers []controller.Handler 23 | } 24 | 25 | // GetHandlers returns all controller REST API endpoints 26 | func (c *RESTAPI) GetHandlers() []controller.Handler { 27 | return c.handlers 28 | } 29 | 30 | // NewRESTAPI returns new controller REST API instance. 31 | func NewRESTAPI(op *zkvote.Operator, opts ...Opt) (*RESTAPI, error) { 32 | restAPIOpts := &allOpts{} 33 | // Apply options 34 | for _, opt := range opts { 35 | opt(restAPIOpts) 36 | } 37 | 38 | var allHandlers []controller.Handler 39 | 40 | sc, err := subjectController.New(op) 41 | if err != nil { 42 | fmt.Print(err) 43 | } 44 | 45 | ic, err := identityController.New(op) 46 | if err != nil { 47 | fmt.Print(err) 48 | } 49 | 50 | allHandlers = append(allHandlers, sc.GetRESTHandlers()...) 51 | allHandlers = append(allHandlers, ic.GetRESTHandlers()...) 52 | 53 | return &RESTAPI{handlers: allHandlers}, nil 54 | } 55 | 56 | // // WithWebhookURLs is an option for setting up a webhook dispatcher which will notify clients of events 57 | // func WithWebhookURLs(webhookURLs ...string) Opt { 58 | // return func(opts *allOpts) { 59 | // opts.webhookURLs = webhookURLs 60 | // } 61 | // } 62 | 63 | // // WithDefaultLabel is an option allowing for the defaultLabel to be set. 64 | // func WithDefaultLabel(defaultLabel string) Opt { 65 | // return func(opts *allOpts) { 66 | // opts.defaultLabel = defaultLabel 67 | // } 68 | // } 69 | -------------------------------------------------------------------------------- /restapi/server.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | "github.com/rs/cors" 9 | zkvote "github.com/unitychain/zkvote-node/zkvote/operator" 10 | ) 11 | 12 | // Server ... 13 | type Server struct { 14 | *RESTAPI 15 | router http.Handler 16 | addr string 17 | } 18 | 19 | // NewServer ... 20 | func NewServer(op *zkvote.Operator, serverAddr string) (*Server, error) { 21 | // get all HTTP REST API handlers available for controller API 22 | restService, err := NewRESTAPI(op) 23 | if err != nil { 24 | return nil, fmt.Errorf("failed to start server: %w", err) 25 | } 26 | 27 | handlers := restService.GetHandlers() 28 | router := mux.NewRouter() 29 | 30 | for _, handler := range handlers { 31 | router.HandleFunc(handler.Path(), handler.Handle()).Methods(handler.Method()) 32 | } 33 | 34 | handler := cors.AllowAll().Handler(router) 35 | 36 | server := &Server{ 37 | RESTAPI: restService, 38 | router: handler, 39 | addr: serverAddr, 40 | } 41 | 42 | return server, nil 43 | } 44 | 45 | // ListenAndServe starts the server using the standard Go HTTP server implementation. 46 | func (s *Server) ListenAndServe() error { 47 | return http.ListenAndServe(s.addr, s.router) 48 | } 49 | -------------------------------------------------------------------------------- /snark/snark_data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unitychain/zkvote-node/60f0e72313344317bf6dc072744678b93beb66ef/snark/snark_data.zip -------------------------------------------------------------------------------- /snark/verification_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth", 3 | "nPublic": 4, 4 | "IC": [ 5 | [ 6 | "6089469007131088921253900864883254663937236698112570561996986728591713837702", 7 | "21665593795332248700780829977591953212946829170845568467445519484380707131355", 8 | "1" 9 | ], 10 | [ 11 | "19153501562621281758040896879515852562863458685170546691277560171683917251349", 12 | "11108039130603009741390216686817428749838156729605342366380762022559779763705", 13 | "1" 14 | ], 15 | [ 16 | "8529384087800742617290125651767068968805367548743608740940502252685980530260", 17 | "6242149106742679777818153728252044690377028977723552079629873833758125448675", 18 | "1" 19 | ], 20 | [ 21 | "7214846473639010699256610387274122137436678671593336350787938831050946590911", 22 | "9681253906154011520610128739459393769792252513526955939556536150058355119547", 23 | "1" 24 | ], 25 | [ 26 | "18610300754574539188111423507021279116387530867167672178535874532887453952196", 27 | "3462677535974159695690234141966205876151814760837848434550950614864691522306", 28 | "1" 29 | ] 30 | ], 31 | "vk_alfa_1": [ 32 | "14911979644349521458763337063704069652060761138484139954235238066231653981066", 33 | "21437127606124754375097316190628279813420033501267902722506696063738368257510", 34 | "1" 35 | ], 36 | "vk_beta_2": [ 37 | [ 38 | "2296380104922247652590142137857116508165365030761409426945299473060233507389", 39 | "306840826053329085138108800028857871964870413096776719672540312276872625625" 40 | ], 41 | [ 42 | "18724149411107241431695508388350670196131630498496287419852573947606649873197", 43 | "17869426396730910703892037638187380766269526997161677689578500741976724608727" 44 | ], 45 | [ 46 | "1", 47 | "0" 48 | ] 49 | ], 50 | "vk_gamma_2": [ 51 | [ 52 | "8622147166521114889942677708686620887526014471195815948742795236846324715006", 53 | "2584396277271144793161670657280174541308492033128079672297396733272179995411" 54 | ], 55 | [ 56 | "18809754889440861269375233951414739702540381436825535327363999629270476455786", 57 | "2226587243782925479277771108137589533198855814584656791680680110324884908261" 58 | ], 59 | [ 60 | "1", 61 | "0" 62 | ] 63 | ], 64 | "vk_delta_2": [ 65 | [ 66 | "3168332349028479746987894437783134046871199923822280856339723472438774208034", 67 | "16144803383944774570842894071978336022150494135196598557365517179577421135520" 68 | ], 69 | [ 70 | "1583571353985083998312446199768913641446063310411523153894263371802378879251", 71 | "18095041610953204001345997870772259981549565227664758128591400756723582548252" 72 | ], 73 | [ 74 | "1", 75 | "0" 76 | ] 77 | ], 78 | "vk_alfabeta_12": [ 79 | [ 80 | [ 81 | "15667866955322340403154068004823034890545363481056628878520093037595550935458", 82 | "18355234004693742618267597314033018521292644796713101653628781465673792941981" 83 | ], 84 | [ 85 | "9389997232742289493680669516074988528948004380494265364020661064982130108486", 86 | "15309798917847295087275454054903210699256820502825230357947154058910520767447" 87 | ], 88 | [ 89 | "1086625388925119466919307893625450313141424507385172515530875087830515970951", 90 | "17171188038147131536630688077966328917233782415687233768237433789146091263960" 91 | ] 92 | ], 93 | [ 94 | [ 95 | "4304406646542536959244798332090562081627489050189134802634867387338533099953", 96 | "8114436234123314315880928307857466707971131313133407810941439608384277245110" 97 | ], 98 | [ 99 | "15788053391464225220869954660453010105742815363962029727362147494761621362292", 100 | "14879769377151945231700251999891081119737619741031763075152519100209586620889" 101 | ], 102 | [ 103 | "15146196472795438168387002532607504981072097094473853764451969483442311215916", 104 | "21728652700951476558543479688664949466985542381078686177557045816724946613052" 105 | ] 106 | ] 107 | ] 108 | } -------------------------------------------------------------------------------- /zkvote/common/crypto/hash_wrapper.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "hash" 5 | "math/big" 6 | ) 7 | 8 | type HashWrapper interface { 9 | hash.Hash 10 | Hash(arr []*big.Int) (*big.Int, error) 11 | } 12 | -------------------------------------------------------------------------------- /zkvote/common/crypto/mimc_wrapper.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/iden3/go-iden3-crypto/mimc7" 7 | ) 8 | 9 | const BlockSize = 64 10 | const Size = 32 11 | const Denominator = 8 12 | 13 | type mimc7Wrapper struct { 14 | data []byte 15 | len uint64 16 | } 17 | 18 | func MiMC7New() HashWrapper { 19 | d := new(mimc7Wrapper) 20 | d.Reset() 21 | return d 22 | } 23 | 24 | func (m *mimc7Wrapper) Write(p []byte) (nn int, err error) { 25 | // m.len = uint64(len(p)) 26 | // // if m.len < 64 { 27 | // // m.len = 64 28 | // // } 29 | // m.data = make([]byte, m.len) 30 | // copy(m.data, p) 31 | 32 | // // left := big.NewInt(0).SetBytes(m.data[:32]) 33 | // // right := big.NewInt(0).SetBytes(m.data[32:]) 34 | // // utils.LogDebugf("* %v/%v", left, right) 35 | return 36 | } 37 | 38 | func (m *mimc7Wrapper) Sum(b []byte) []byte { 39 | return nil 40 | // left := big.NewInt(0).SetBytes(m.data[:int(m.len/2)]) 41 | // right := big.NewInt(0).SetBytes(m.data[int(m.len/2):]) 42 | // if m.len <= 32 { 43 | // left = big.NewInt(0).SetBytes(m.data) 44 | // right = big.NewInt(0) 45 | // } else if m.len <= 64 { 46 | // left = big.NewInt(0).SetBytes(m.data[:32]) 47 | // right = big.NewInt(0).SetBytes(m.data[32:]) 48 | // } 49 | 50 | // // TODO: Div(8) is a workaround mimc7 in golang will check finite field Q 51 | // left = left.Div(left, big.NewInt(Denominator)) 52 | // right = right.Div(right, big.NewInt(Denominator)) 53 | 54 | // h, err := mimc7.Hash([]*big.Int{left, right}, nil) 55 | // m.Reset() 56 | // if err != nil { 57 | // utils.LogErrorf("mimc hash error, %v", err.Error()) 58 | // return nil 59 | // } 60 | // // utils.LogDebugf("* %v/%v", left, right) 61 | // // utils.LogDebugf(" %v", h) 62 | // return h.Bytes() 63 | } 64 | 65 | // Reset resets the Hash to its initial state. 66 | func (m *mimc7Wrapper) Reset() { 67 | m.data = nil 68 | m.len = 0 69 | } 70 | 71 | // Size returns the number of bytes Sum will return. 72 | func (m *mimc7Wrapper) Size() int { return Size } 73 | 74 | // BlockSize returns the hash's underlying block size. 75 | // The Write method must be able to accept any amount 76 | // of data, but it may operate more efficiently if all writes 77 | // are a multiple of the block size. 78 | func (m *mimc7Wrapper) BlockSize() int { return BlockSize } 79 | 80 | func (m *mimc7Wrapper) Hash(arr []*big.Int) (*big.Int, error) { 81 | return m.hash(arr[:len(arr)-1], arr[len(arr)-1]) 82 | } 83 | 84 | func (m *mimc7Wrapper) hash(arr []*big.Int, key *big.Int) (*big.Int, error) { 85 | for i, a := range arr { 86 | // TODO: Div(8) is a workaround, mimc7 in golang will check finite field, Q. 87 | arr[i] = new(big.Int).Div(a, big.NewInt(Denominator)) 88 | } 89 | if nil == key { 90 | key = big.NewInt(0) 91 | } 92 | return mimc7.Hash(arr, key) 93 | } 94 | -------------------------------------------------------------------------------- /zkvote/common/store/cache.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 7 | "github.com/unitychain/zkvote-node/zkvote/model/ballot" 8 | "github.com/unitychain/zkvote-node/zkvote/model/identity" 9 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 10 | ) 11 | 12 | // Cache ... 13 | type Cache struct { 14 | collectedSubjects subject.Map 15 | createdSubjects subject.Map 16 | ballotMap map[subject.HashHex]ballot.Map 17 | idMap map[subject.HashHex]identity.Set 18 | } 19 | 20 | // NewCache ... 21 | func NewCache() (*Cache, error) { 22 | return &Cache{ 23 | collectedSubjects: subject.NewMap(), 24 | createdSubjects: subject.NewMap(), 25 | ballotMap: make(map[subject.HashHex]ballot.Map), 26 | idMap: make(map[subject.HashHex]identity.Set), 27 | }, nil 28 | } 29 | 30 | func (c *Cache) isExistedSubject(sHex subject.HashHex) bool { 31 | for k := range c.collectedSubjects { 32 | if strings.EqualFold(utils.Remove0x(k.String()), utils.Remove0x(sHex.String())) { 33 | return true 34 | } 35 | } 36 | for k := range c.createdSubjects { 37 | if strings.EqualFold(utils.Remove0x(k.String()), utils.Remove0x(sHex.String())) { 38 | return true 39 | } 40 | } 41 | return false 42 | } 43 | 44 | // InsertColletedSubject . 45 | func (c *Cache) InsertColletedSubject(k subject.HashHex, v *subject.Subject) { 46 | if c.isExistedSubject(k) { 47 | return 48 | } 49 | c.collectedSubjects[k] = v 50 | } 51 | 52 | //GetCollectedSubjects . 53 | func (c *Cache) GetCollectedSubjects() subject.Map { 54 | return c.collectedSubjects 55 | } 56 | 57 | // GetACollectedSubject ... 58 | func (c *Cache) GetACollectedSubject(k subject.HashHex) *subject.Subject { 59 | return c.collectedSubjects[k] 60 | } 61 | 62 | // InsertCreatedSubject . 63 | func (c *Cache) InsertCreatedSubject(k subject.HashHex, v *subject.Subject) { 64 | if c.isExistedSubject(k) { 65 | return 66 | } 67 | c.createdSubjects[k] = v 68 | } 69 | 70 | //GetCreatedSubjects . 71 | func (c *Cache) GetCreatedSubjects() subject.Map { 72 | return c.createdSubjects 73 | } 74 | 75 | // GetACreatedSubject ... 76 | func (c *Cache) GetACreatedSubject(k subject.HashHex) *subject.Subject { 77 | return c.createdSubjects[k] 78 | } 79 | 80 | // GetBallotSet . 81 | func (c *Cache) GetBallotSet(subHashHex subject.HashHex) ballot.Map { 82 | return c.ballotMap[subHashHex] 83 | } 84 | 85 | // InsertBallotSet . 86 | func (c *Cache) InsertBallotSet(subHashHex subject.HashHex, ballotMap ballot.Map) { 87 | _, ok := c.ballotMap[subHashHex] 88 | if !ok { 89 | c.ballotMap[subHashHex] = ballot.NewMap() 90 | } 91 | c.ballotMap[subHashHex] = ballotMap 92 | } 93 | 94 | // InsertBallot . 95 | func (c *Cache) InsertBallot(subHashHex subject.HashHex, ba *ballot.Ballot) { 96 | _, ok := c.ballotMap[subHashHex] 97 | if !ok { 98 | c.ballotMap[subHashHex] = ballot.NewMap() 99 | } 100 | c.ballotMap[subHashHex][ba.NullifierHashHex()] = ba 101 | } 102 | 103 | func (c *Cache) InsertIdentitySet(subHashHex subject.HashHex, idSet identity.Set) { 104 | _, ok := c.idMap[subHashHex] 105 | if !ok { 106 | c.idMap[subHashHex] = identity.NewSet() 107 | } 108 | c.idMap[subHashHex] = idSet 109 | } 110 | 111 | func (c *Cache) InsertIdentity(subHashHex subject.HashHex, id identity.Identity) { 112 | _, ok := c.idMap[subHashHex] 113 | if !ok { 114 | c.idMap[subHashHex] = identity.NewSet() 115 | } 116 | c.idMap[subHashHex][id] = id.String() 117 | } 118 | 119 | func (c *Cache) GetIdentitySet(subHashHex subject.HashHex) identity.Set { 120 | return c.idMap[subHashHex] 121 | } 122 | -------------------------------------------------------------------------------- /zkvote/common/store/node_validator.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | // NodeValidator ... 4 | type NodeValidator struct{} 5 | 6 | // Validate ... 7 | func (nv NodeValidator) Validate(ket string, value []byte) error { 8 | return nil 9 | } 10 | 11 | // Select ... 12 | func (nv NodeValidator) Select(key string, values [][]byte) (int, error) { 13 | return 0, nil 14 | } 15 | -------------------------------------------------------------------------------- /zkvote/common/store/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ipfs/go-datastore" 7 | dht "github.com/libp2p/go-libp2p-kad-dht" 8 | "github.com/whyrusleeping/base32" 9 | ) 10 | 11 | // Store ... 12 | type Store struct { 13 | dht *dht.IpfsDHT 14 | db datastore.Batching 15 | } 16 | 17 | // NewStore ... 18 | func NewStore(dht *dht.IpfsDHT, db datastore.Batching) (*Store, error) { 19 | return &Store{ 20 | dht: dht, 21 | db: db, 22 | }, nil 23 | } 24 | 25 | // func (s *Store) InsertSubject(HashHex) 26 | 27 | // PutDHT ... 28 | func (store *Store) PutDHT(k, v string) error { 29 | ctx := context.Background() 30 | err := store.dht.PutValue(ctx, k, []byte(v)) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | return nil 36 | } 37 | 38 | // GetDHT ... 39 | func (store *Store) GetDHT(k string) ([]byte, error) { 40 | ctx := context.Background() 41 | 42 | vb, err := store.dht.GetValue(ctx, k) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return vb, nil 47 | } 48 | 49 | // PutLocal ... 50 | func (store *Store) PutLocal(k, v string) error { 51 | err := store.db.Put(mkDsKey(k), []byte(v)) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | return nil 57 | } 58 | 59 | // GetLocal ... 60 | func (store *Store) GetLocal(k string) (string, error) { 61 | vb, err := store.db.Get(mkDsKey(k)) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | return string(vb), nil 67 | } 68 | 69 | func mkDsKey(s string) datastore.Key { 70 | return datastore.NewKey(base32.RawStdEncoding.EncodeToString([]byte(s))) 71 | } 72 | -------------------------------------------------------------------------------- /zkvote/common/utils/constants.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | const ClientVersion = "zkvote/0.0.1" 4 | -------------------------------------------------------------------------------- /zkvote/common/utils/logger.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "os" 8 | "runtime" 9 | "strconv" 10 | "strings" 11 | 12 | logging "github.com/op/go-logging" 13 | ) 14 | 15 | var logger = logging.MustGetLogger("logs") 16 | 17 | // var formatConsole = logging.MustStringFormatter( 18 | // `%{color}%{time:15:04:05.00} [%{level:.5s}] %{color:reset}%{message}`, // (%{shortfile} 19 | // ) 20 | var formatConsole = logging.MustStringFormatter( 21 | `%{color}%{time:15:04:05.000} %{color:reset}%{message}`, // (%{shortfile} 22 | ) 23 | 24 | var formatFile = logging.MustStringFormatter( 25 | `%{color}%{time:2006-01-02 15:04:05.000} [%{level:.5s}] %{color:reset}%{message}`, // (%{shortfunc}___%{shortfile} 26 | ) 27 | 28 | var file *os.File = nil 29 | 30 | // OpenLog ... 31 | func OpenLog() { 32 | if file != nil { 33 | return 34 | } 35 | 36 | var err error 37 | file, err = os.OpenFile("logs.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) 38 | if err != nil { 39 | fmt.Printf("error opening file: %v", err) 40 | } 41 | //defer f.Close() 42 | 43 | backendFile := logging.NewLogBackend(file, "", 0) 44 | backendFileFormatter := logging.NewBackendFormatter(backendFile, formatFile) 45 | 46 | backendConsole := logging.NewLogBackend(os.Stdout, "", 0) 47 | backendConsoleFormatter := logging.NewBackendFormatter(backendConsole, formatConsole) 48 | backendConsoleLeveled := logging.AddModuleLevel(backendConsoleFormatter) 49 | //backendConsoleLeveled.SetLevel(logging.WARNING, "") 50 | 51 | logging.SetBackend(backendFileFormatter, backendConsoleLeveled) 52 | 53 | } 54 | 55 | // CloseLog ... 56 | func CloseLog() { 57 | if file != nil { 58 | file.Close() 59 | } 60 | } 61 | 62 | // AssertError ... 63 | func AssertError(err error, msg string) bool { 64 | if err == nil { 65 | return false 66 | } 67 | LogErrorf("%s - [%s]", msg, err.Error()) 68 | return true 69 | } 70 | 71 | // AssertWarn ... 72 | func AssertWarn(err error, msg string) bool { 73 | if err == nil { 74 | return false 75 | } 76 | LogWarningf("%s - [%s]", msg, err.Error()) 77 | return true 78 | } 79 | 80 | func getFileNLine() string { 81 | _, file, line, ok := runtime.Caller(2) 82 | if !ok { 83 | file = "???" 84 | line = 0 85 | } 86 | return fmt.Sprintf("%s:%d", filepath.Base(file), line) 87 | } 88 | 89 | // LogDebugf ... 90 | func LogDebugf(s string, args ...interface{}) { 91 | logger.Debugf("[%d-%d] %s (%s)", os.Getpid(), Goid(), fmt.Sprintf(s, args...), getFileNLine()) 92 | } 93 | 94 | // LogDebug ... 95 | func LogDebug(s string) { 96 | logger.Debugf("[%d-%d] %s (%s)", os.Getpid(), Goid(), s, getFileNLine()) 97 | } 98 | 99 | // LogInfof ... 100 | func LogInfof(s string, args ...interface{}) { 101 | logger.Infof("[%d-%d] %s (%s)", os.Getpid(), Goid(), fmt.Sprintf(s, args...), getFileNLine()) 102 | } 103 | 104 | // LogInfo ... 105 | func LogInfo(s string) { 106 | logger.Infof("[%d-%d] %s (%s)", os.Getpid(), Goid(), s, getFileNLine()) 107 | } 108 | 109 | // LogWarningf ... 110 | func LogWarningf(s string, args ...interface{}) { 111 | logger.Warningf("[%d-%d] %s (%s)", os.Getpid(), Goid(), fmt.Sprintf(s, args...), getFileNLine()) 112 | } 113 | 114 | // LogWarning ... 115 | func LogWarning(s string) { 116 | logger.Warningf("[%d-%d] %s (%s)", os.Getpid(), Goid(), s, getFileNLine()) 117 | } 118 | 119 | // LogErrorf ... 120 | func LogErrorf(s string, args ...interface{}) { 121 | logger.Errorf("[%d-%d] %s (%s)", os.Getpid(), Goid(), fmt.Sprintf(s, args...), getFileNLine()) 122 | } 123 | 124 | // LogError ... 125 | func LogError(s string) { 126 | logger.Errorf("[%d-%d] %s (%s)", os.Getpid(), Goid(), s, getFileNLine()) 127 | } 128 | 129 | // LogFatalf ... 130 | func LogFatalf(s string, args ...interface{}) { 131 | logger.Fatalf("[%d-%d] %s (%s)", os.Getpid(), Goid(), fmt.Sprintf(s, args...), getFileNLine()) 132 | } 133 | 134 | // LogFatal ... 135 | func LogFatal(s string) { 136 | logger.Fatalf("[%d-%d] %s (%s)", os.Getpid(), Goid(), s, getFileNLine()) 137 | } 138 | 139 | func Goid() int { 140 | defer func() { 141 | if err := recover(); err != nil { 142 | logger.Fatal("panic recover:panic info:%v", err) 143 | } 144 | }() 145 | 146 | var buf [64]byte 147 | n := runtime.Stack(buf[:], false) 148 | idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] 149 | id, err := strconv.Atoi(idField) 150 | if err != nil { 151 | panic(fmt.Sprintf("cannot get goroutine id: %v", err)) 152 | } 153 | return id 154 | } 155 | -------------------------------------------------------------------------------- /zkvote/common/utils/type_converter.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | h "encoding/hex" 5 | "math/big" 6 | "strings" 7 | ) 8 | 9 | // CheckHex . 10 | func CheckHex(s string) error { 11 | _, err := h.DecodeString(Remove0x(s)) 12 | return err 13 | } 14 | 15 | // Remove0x ... 16 | func Remove0x(key string) string { 17 | if strings.HasPrefix(key, "0x") { 18 | return key[2:len(key)] 19 | } 20 | return key 21 | } 22 | 23 | // Prepend0x ... 24 | func Prepend0x(key string) string { 25 | if strings.HasPrefix(key, "0x") { 26 | return key 27 | } 28 | return "0x" + key 29 | } 30 | 31 | func GetBigIntFromHexString(hex string) *big.Int { 32 | b, _ := big.NewInt(0).SetString(Remove0x(hex), 16) 33 | return b 34 | } 35 | 36 | func GetHexStringFromBigInt(b *big.Int) string { 37 | return Prepend0x(h.EncodeToString(b.Bytes())) 38 | } 39 | 40 | func GetBytesFromHexString(hex string) []byte { 41 | b, _ := h.DecodeString(Remove0x(hex)) 42 | return b 43 | } 44 | 45 | func GetHexStringFromBytes(b []byte) string { 46 | return GetHexStringFromBigInt(big.NewInt(0).SetBytes(b)) 47 | } 48 | -------------------------------------------------------------------------------- /zkvote/model/ballot/ballot.go: -------------------------------------------------------------------------------- 1 | package ballot 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "encoding/json" 7 | "fmt" 8 | 9 | "github.com/arnaucube/go-snark/externalVerif" 10 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 11 | ) 12 | 13 | // Ballot ... 14 | type Ballot struct { 15 | Root string `json:"root"` 16 | NullifierHash string `json:"nullifier_hash"` 17 | Proof *externalVerif.CircomProof `json:"proof"` 18 | PublicSignal []string `json:"public_signal"` //root, nullifiers_hash, signal_hash, external_nullifier 19 | } 20 | 21 | // Hash ... 22 | type Hash []byte 23 | 24 | // HashHex ... 25 | type HashHex string 26 | 27 | // NullifierHashHex ... 28 | type NullifierHashHex string 29 | 30 | // NewBallot ... 31 | func NewBallot(proof string) (*Ballot, error) { 32 | if 0 == len(proof) { 33 | utils.LogWarningf("invalid input:\n %s", proof) 34 | return nil, fmt.Errorf("invalid input") 35 | } 36 | 37 | var b Ballot 38 | err := json.Unmarshal([]byte(proof), &b) 39 | if err != nil { 40 | utils.LogErrorf("parse proof: unmarshal error %v", err.Error()) 41 | return nil, err 42 | } 43 | return &b, nil 44 | } 45 | 46 | // Byte ... 47 | func (b *Ballot) Byte() ([]byte, error) { 48 | return json.Marshal(b) 49 | } 50 | 51 | // Byte ... 52 | func (h Hash) Byte() []byte { return []byte(h) } 53 | 54 | // Hash ... 55 | func (b *Ballot) Hash() *Hash { 56 | bByte, _ := b.Byte() 57 | h := sha256.Sum256(bByte) 58 | result := Hash(h[:]) 59 | return &result 60 | } 61 | 62 | // NullifierHashHex ... 63 | func (b *Ballot) NullifierHashHex() NullifierHashHex { 64 | // Convert to hex if needed 65 | return NullifierHashHex(b.NullifierHash) 66 | } 67 | 68 | // JSON . 69 | func (b *Ballot) JSON() (string, error) { 70 | d, e := b.Byte() 71 | return string(d), e 72 | } 73 | 74 | // Hex ... 75 | func (h Hash) Hex() HashHex { 76 | return HashHex(hex.EncodeToString(h.Byte())) 77 | } 78 | 79 | // Map ... 80 | type Map map[NullifierHashHex]*Ballot 81 | 82 | // NewMap ... 83 | func NewMap() Map { 84 | return Map(make(map[NullifierHashHex]*Ballot)) 85 | } 86 | -------------------------------------------------------------------------------- /zkvote/model/context/context.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/libp2p/go-libp2p-core/host" 8 | "github.com/unitychain/zkvote-node/zkvote/common/store" 9 | ) 10 | 11 | type Context struct { 12 | Mutex *sync.RWMutex 13 | Host host.Host 14 | Store *store.Store 15 | Cache *store.Cache 16 | Ctx *context.Context 17 | } 18 | 19 | func NewContext(mutex *sync.RWMutex, host host.Host, store *store.Store, cache *store.Cache, ctx *context.Context) *Context { 20 | return &Context{ 21 | Mutex: mutex, 22 | Host: host, 23 | Store: store, 24 | Cache: cache, 25 | Ctx: ctx, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /zkvote/model/identity/identity.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 7 | ) 8 | 9 | // Identity ... 10 | type Identity string 11 | 12 | // NewIdentity ... 13 | func NewIdentity(commitment string) *Identity { 14 | // TODO: Change the encoding tp hex if needed 15 | if err := utils.CheckHex(commitment); err != nil { 16 | return nil 17 | } 18 | id := Identity(utils.Remove0x(commitment)) 19 | return &id 20 | } 21 | 22 | // NewIdentityFromBytes ... 23 | func NewIdentityFromBytes(bytes []byte) *Identity { 24 | hex := utils.GetHexStringFromBytes(bytes) 25 | id := Identity(hex) 26 | return &id 27 | } 28 | 29 | // Hash ... 30 | type Hash []byte 31 | 32 | // Byte ... 33 | func (id *Identity) Byte() []byte { return utils.GetBytesFromHexString(id.String()) } 34 | 35 | // String ... 36 | func (id *Identity) String() string { return string(*id) } 37 | 38 | // Hex ... 39 | func (id *Identity) Hex() string { 40 | return utils.GetHexStringFromBigInt(big.NewInt(0).SetBytes(id.Byte())) 41 | } 42 | 43 | // Equal . 44 | func (id *Identity) Equal(othID *Identity) bool { 45 | self := big.NewInt(0).SetBytes(id.Byte()) 46 | oth := big.NewInt(0).SetBytes(othID.Byte()) 47 | return 0 == self.Cmp(oth) 48 | } 49 | 50 | // PathElement ... 51 | func (id Identity) PathElement() *IdPathElement { 52 | // TODO: do check 53 | bigValue := big.NewInt(0).SetBytes(id.Byte()) 54 | return NewIdPathElement(NewTreeContent(bigValue)) 55 | } 56 | 57 | // Set ... 58 | type Set map[Identity]string 59 | 60 | // NewSet ... 61 | func NewSet() Set { 62 | result := Set(make(map[Identity]string)) 63 | return result 64 | } 65 | 66 | // 67 | // IdPathElement 68 | // 69 | 70 | type IdPathElement struct { 71 | e *TreeContent 72 | } 73 | 74 | func NewIdPathElement(t *TreeContent) *IdPathElement { 75 | return &IdPathElement{t} 76 | } 77 | func (i *IdPathElement) String() string { 78 | if nil == i.e || 0 == i.e.BigInt().Cmp(big.NewInt(0)) { 79 | return "0" 80 | } 81 | return i.e.String() 82 | } 83 | func (i *IdPathElement) Hex() string { 84 | if nil == i.e || 0 == i.e.BigInt().Cmp(big.NewInt(0)) { 85 | return "0x0" 86 | } 87 | return i.e.Hex() 88 | } 89 | func (i *IdPathElement) Equal(oth IdPathElement) bool { 90 | b, _ := i.e.Equals(oth.e) 91 | return b 92 | } 93 | func (i *IdPathElement) BigInt() *big.Int { return i.e.BigInt() } 94 | func (i *IdPathElement) Content() TreeContent { return *i.e } 95 | -------------------------------------------------------------------------------- /zkvote/model/identity/merkle_tree.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/big" 7 | 8 | merkletree "github.com/cbergoon/merkletree" 9 | hashWrapper "github.com/unitychain/zkvote-node/zkvote/common/crypto" 10 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 11 | ) 12 | 13 | // 14 | // TreeContent 15 | // 16 | 17 | // TreeContent ... 18 | type TreeContent struct { 19 | x *big.Int 20 | } 21 | 22 | func NewTreeContent(value *big.Int) *TreeContent { 23 | return &TreeContent{value} 24 | } 25 | 26 | //CalculateHash hashes the values of a TreeContent 27 | func (t TreeContent) CalculateHash() ([]byte, error) { 28 | return t.x.Bytes(), nil 29 | // if 0 == t.x.Cmp(big.NewInt(0)) { 30 | // return []byte{0}, nil 31 | // } 32 | // // return mimc7.MIMC7Hash(t.x, big.NewInt(0)).Bytes(), nil 33 | 34 | // return crypto.Keccak256(t.x.Bytes()), nil 35 | } 36 | 37 | //Equals tests for equality of two Contents 38 | func (t TreeContent) Equals(other merkletree.Content) (bool, error) { 39 | // return t.x.String() == other.(TreeContent).x.String(), nil 40 | return 0 == t.x.Cmp(other.(TreeContent).x), nil 41 | } 42 | 43 | func (t TreeContent) String() string { 44 | if nil == t.x || 0 == t.x.Cmp(big.NewInt(0)) { 45 | return "0" 46 | } 47 | return t.x.String() 48 | } 49 | 50 | func (t TreeContent) Hex() string { 51 | if nil == t.x || 0 == t.x.Cmp(big.NewInt(0)) { 52 | return "0x0" 53 | } 54 | return utils.GetHexStringFromBigInt(t.x) 55 | } 56 | 57 | func (t TreeContent) Bytes() []byte { 58 | return t.x.Bytes() 59 | } 60 | 61 | func (t TreeContent) BigInt() *big.Int { 62 | return t.x 63 | } 64 | 65 | // 66 | // MerkleTree 67 | // 68 | 69 | // MerkleTree ... 70 | type MerkleTree struct { 71 | levels uint8 72 | nextIndex uint 73 | 74 | root *TreeContent 75 | content []merkletree.Content 76 | mapContent map[merkletree.Content]uint 77 | 78 | hashStrategy hashWrapper.HashWrapper 79 | } 80 | 81 | // NewMerkleTree ... 82 | func NewMerkleTree(levels uint8) (*MerkleTree, error) { 83 | 84 | // create an empty tree with zeros 85 | var content []merkletree.Content 86 | numIndexes := int(math.Pow(2, float64(levels))) 87 | for i := 0; i < numIndexes; i++ { 88 | content = append(content, TreeContent{big.NewInt(0)}) 89 | } 90 | tree := &MerkleTree{ 91 | levels: levels, 92 | nextIndex: 0, 93 | content: content, 94 | hashStrategy: hashWrapper.MiMC7New(), 95 | mapContent: make(map[merkletree.Content]uint), 96 | } 97 | 98 | root, err := tree.calculateRoot() 99 | if err != nil { 100 | return nil, err 101 | } 102 | tree.root = root 103 | 104 | utils.LogInfof("total elements %d, init root: %v", numIndexes, root) 105 | return tree, nil 106 | } 107 | 108 | // Insert : insert into to the merkle tree 109 | func (m *MerkleTree) Insert(value TreeContent) (int, error) { 110 | // if value == nil { 111 | // return -1, fmt.Errorf("invalid input value") 112 | // } 113 | if m.IsExisted(&value) { 114 | return -1, fmt.Errorf("value existed, %v", value) 115 | } 116 | 117 | currentIndex := m.nextIndex 118 | m.addContent(currentIndex, value) 119 | 120 | root, err := m.calculateRoot() 121 | if err != nil { 122 | return -1, err 123 | } 124 | m.root = root 125 | m.nextIndex++ 126 | utils.LogInfof("new merkle root: %v", root) 127 | 128 | return int(currentIndex), nil 129 | } 130 | 131 | // Update : update a leaf of this merkle tree 132 | func (m *MerkleTree) Update(index uint, oldValue, newValue TreeContent) error { 133 | 134 | if b, _ := oldValue.Equals(newValue); b { 135 | return fmt.Errorf("old and new value are the same") 136 | } 137 | if !m.IsExisted(&oldValue) { 138 | return fmt.Errorf("old value not existed, %v", oldValue) 139 | } 140 | if eq, _ := m.content[index].Equals(oldValue); !eq { 141 | // utils.LogErrorf("value of the index is not matched old value.") 142 | return fmt.Errorf("value of the index is not matched old value") 143 | } 144 | 145 | m.addContent(index, newValue) 146 | root, err := m.calculateRoot() 147 | if err != nil { 148 | return err 149 | } 150 | m.root = root 151 | utils.LogInfof("new root: %v", root) 152 | 153 | return nil 154 | } 155 | 156 | // GetRoot : get current merkle root 157 | func (m *MerkleTree) GetRoot() *TreeContent { 158 | return m.root 159 | } 160 | 161 | // GetPath : get merkle path of a leaf 162 | func (m *MerkleTree) GetPath(value *TreeContent) []byte { 163 | 164 | idx := m.GetIndexByValue(value) 165 | if idx == -1 { 166 | utils.LogWarningf("Can NOT find index of value, %v", value) 167 | return nil 168 | } 169 | paths := make([]byte, m.levels) 170 | 171 | for i := 0; i < int(m.levels); i++ { 172 | if 0 == idx%2 { 173 | paths[i] = 0 174 | } else { 175 | paths[i] = 1 176 | } 177 | idx /= 2 178 | } 179 | 180 | return paths 181 | } 182 | 183 | // GetIntermediateValues : get all intermediate values of a leaf 184 | func (m *MerkleTree) GetIntermediateValues(value *TreeContent) ([]*TreeContent, []int, *TreeContent) { 185 | 186 | var idx int 187 | if nil == value { 188 | idx = 0 189 | } else { 190 | idx = m.GetIndexByValue(value) 191 | if -1 == idx { 192 | utils.LogWarningf("Can NOT find index of value, %v", value) 193 | return nil, nil, nil 194 | } 195 | } 196 | 197 | currentIdx := idx 198 | imv := make([]*TreeContent, m.levels) 199 | imi := make([]int, m.levels) 200 | tree := make([][]*TreeContent, m.levels) 201 | for i := 0; i < int(m.levels); i++ { 202 | 203 | numElemofLevel := int(math.Pow(2, float64(int(m.levels)-i))) 204 | valuesOfLevel := make([]*TreeContent, numElemofLevel) 205 | imi[i] = currentIdx % 2 206 | for j := 0; j < numElemofLevel; j++ { 207 | if 0 == i { 208 | h, _ := m.content[j].CalculateHash() 209 | valuesOfLevel[j] = &TreeContent{big.NewInt(0).SetBytes(h)} 210 | 211 | } else { 212 | h, err := m.hashStrategy.Hash([]*big.Int{tree[i-1][2*j].x, tree[i-1][2*j+1].x, big.NewInt(0)}) 213 | if err != nil { 214 | utils.LogFatalf("ERROR: calculate mimc7 error, %v", err.Error()) 215 | return nil, nil, nil 216 | } 217 | valuesOfLevel[j] = &TreeContent{h} 218 | } 219 | } 220 | 221 | if 0 == currentIdx%2 { 222 | imv[i] = valuesOfLevel[currentIdx+1] 223 | } else { 224 | imv[i] = valuesOfLevel[currentIdx-1] 225 | } 226 | 227 | tree[i] = valuesOfLevel 228 | currentIdx = int(currentIdx / 2) 229 | } 230 | root, err := m.hashStrategy.Hash([]*big.Int{tree[m.levels-1][0].x, tree[m.levels-1][1].x, big.NewInt(0)}) 231 | if err != nil { 232 | utils.LogFatalf("ERROR: calculate root through mimc7 error, %v", err.Error()) 233 | return nil, nil, nil 234 | } 235 | return imv, imi, &TreeContent{root} 236 | } 237 | 238 | // GetAllContent . 239 | func (m *MerkleTree) GetAllContent() []*TreeContent { 240 | lenWithContent := len(m.mapContent) 241 | ids := make([]*TreeContent, lenWithContent) 242 | for i := 0; i < lenWithContent; i++ { 243 | ids[i] = &TreeContent{m.content[i].(TreeContent).x} 244 | } 245 | return ids 246 | } 247 | 248 | // IsExisted ... 249 | func (m *MerkleTree) IsExisted(value *TreeContent) bool { 250 | if 0 <= m.GetIndexByValue(value) { 251 | return true 252 | } 253 | return false 254 | } 255 | 256 | // GetIndexByValue . 257 | func (m *MerkleTree) GetIndexByValue(value *TreeContent) int { 258 | for i, c := range m.content { 259 | if eq, _ := c.Equals(*value); eq { 260 | utils.LogDebugf("Got index, %d", i) 261 | return i 262 | } 263 | } 264 | return -1 265 | } 266 | 267 | // Len . 268 | func (m *MerkleTree) Len() int { 269 | return int(m.nextIndex) 270 | } 271 | 272 | // 273 | // Internal functions 274 | // 275 | 276 | func (m *MerkleTree) addContent(idx uint, value TreeContent) { 277 | m.content[idx] = value 278 | m.mapContent[value] = idx 279 | } 280 | 281 | func (m *MerkleTree) calculateRoot() (*TreeContent, error) { 282 | 283 | _, _, root := m.GetIntermediateValues(nil) 284 | return root, nil 285 | } 286 | -------------------------------------------------------------------------------- /zkvote/model/identity/merkle_tree_test.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | const idCommitment string = "17610192990552485611214212559447309706539616482639833145108503521837267798810" 12 | 13 | func TestInitValue(t *testing.T) { 14 | tree, err := NewMerkleTree(10) 15 | assert.Nil(t, err, "new merkle tree instance error") 16 | 17 | expectedRoot, _ := big.NewInt(0).SetString("3045810468960591087191210641638281907572011303565471692703782899879208219595", 10) 18 | assert.Equal(t, expectedRoot, tree.GetRoot().BigInt()) 19 | } 20 | 21 | func TestInsert(t *testing.T) { 22 | tree, err := NewMerkleTree(10) 23 | assert.Nil(t, err, "new merkle tree instance error") 24 | 25 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 26 | idx, err := tree.Insert(&TreeContent{idc}) 27 | assert.Nil(t, err, "insert error") 28 | assert.Equal(t, 0, idx) 29 | 30 | expectedRoot, _ := big.NewInt(0).SetString("1603056697422863699573935817849018482475219731925672640724433076363786113", 10) 31 | assert.Equal(t, expectedRoot, tree.GetRoot().BigInt()) 32 | } 33 | 34 | func TestInsert_10IDs(t *testing.T) { 35 | tree, err := NewMerkleTree(10) 36 | assert.Nil(t, err, "new merkle tree instance error") 37 | 38 | for i := 0; i < 10; i++ { 39 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 40 | idx, err := tree.Insert(&TreeContent{idc}) 41 | tree.GetIntermediateValues(&TreeContent{idc}) 42 | 43 | assert.Nil(t, err, "insert error") 44 | assert.Equal(t, i, idx) 45 | } 46 | assert.Equal(t, "14980283469360920217403527533153996284933222989994708885209730188977033874534", tree.GetRoot().String()) 47 | // root := tree.GetRoot().String() 48 | // fmt.Println("root, ", root) 49 | } 50 | 51 | func TestInsert_Double(t *testing.T) { 52 | tree, err := NewMerkleTree(10) 53 | assert.Nil(t, err, "new identity instance error") 54 | 55 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 56 | idx, err := tree.Insert(&TreeContent{idc}) 57 | assert.Nil(t, err, "Insert error") 58 | assert.Equal(t, 0, idx) 59 | 60 | idx, err = tree.Insert(&TreeContent{idc}) 61 | assert.NotNil(t, err, "should not Insert successfully") 62 | } 63 | 64 | func TestTreeUpdate(t *testing.T) { 65 | tree, err := NewMerkleTree(10) 66 | assert.Nil(t, err, "new identity instance error") 67 | 68 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 69 | idx, err := tree.Insert(&TreeContent{idc}) 70 | assert.Nil(t, err, "Insert error") 71 | assert.Equal(t, 0, idx) 72 | 73 | err = tree.Update(uint(idx), &TreeContent{idc}, &TreeContent{big.NewInt(100)}) 74 | assert.Nil(t, err, "update error") 75 | assert.Equal(t, "5860034871856545585778554733050920915757269722014984975581566802595274325429", tree.GetRoot().String()) 76 | 77 | } 78 | 79 | func TestTreeUpdate_IncorrectIdx(t *testing.T) { 80 | tree, err := NewMerkleTree(10) 81 | assert.Nil(t, err, "new identity instance error") 82 | 83 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 84 | idx, err := tree.Insert(&TreeContent{idc}) 85 | assert.Nil(t, err, "Insert error") 86 | assert.Equal(t, 0, idx) 87 | 88 | err = tree.Update(1, &TreeContent{idc}, &TreeContent{big.NewInt(100)}) 89 | assert.NotNil(t, err, "update error") 90 | } 91 | 92 | func TestTreeUpdate_IncorrectContent(t *testing.T) { 93 | tree, err := NewMerkleTree(10) 94 | assert.Nil(t, err, "new identity instance error") 95 | 96 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 97 | idx, err := tree.Insert(&TreeContent{idc}) 98 | assert.Nil(t, err, "Insert error") 99 | assert.Equal(t, 0, idx) 100 | 101 | err = tree.Update(uint(idx), &TreeContent{big.NewInt(100)}, &TreeContent{big.NewInt(100)}) 102 | assert.NotNil(t, err, "update error") 103 | } 104 | 105 | func TestGetAllContent(t *testing.T) { 106 | tree, err := NewMerkleTree(10) 107 | assert.Nil(t, err, "new identity instance error") 108 | 109 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 110 | idx, err := tree.Insert(&TreeContent{idc}) 111 | assert.Nil(t, err, "Insert error") 112 | assert.Equal(t, 0, idx) 113 | 114 | c := tree.GetAllContent() 115 | assert.NotNil(t, c) 116 | } 117 | 118 | func TestIsExisted(t *testing.T) { 119 | tree, err := NewMerkleTree(10) 120 | assert.Nil(t, err, "new identity instance error") 121 | 122 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 123 | idx, err := tree.Insert(&TreeContent{idc}) 124 | assert.Nil(t, err, "Insert error") 125 | assert.Equal(t, 0, idx) 126 | 127 | b := tree.IsExisted(&TreeContent{idc}) 128 | assert.True(t, b) 129 | } 130 | 131 | func TestGetIndexByValue(t *testing.T) { 132 | tree, err := NewMerkleTree(10) 133 | assert.Nil(t, err, "new identity instance error") 134 | 135 | for i := 0; i < 10; i++ { 136 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 137 | idx, err := tree.Insert(&TreeContent{idc}) 138 | assert.Nil(t, err, "insert error") 139 | 140 | index := tree.GetIndexByValue(&TreeContent{idc}) 141 | 142 | assert.Equal(t, idx, index) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /zkvote/model/pb/zkvote.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: zkvote.proto 3 | 4 | package protocols_zkvote 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | // a protocol define a set of reuqest and responses 24 | type SubjectRequest struct { 25 | Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` 26 | // method specific data 27 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 28 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 29 | XXX_unrecognized []byte `json:"-"` 30 | XXX_sizecache int32 `json:"-"` 31 | } 32 | 33 | func (m *SubjectRequest) Reset() { *m = SubjectRequest{} } 34 | func (m *SubjectRequest) String() string { return proto.CompactTextString(m) } 35 | func (*SubjectRequest) ProtoMessage() {} 36 | func (*SubjectRequest) Descriptor() ([]byte, []int) { 37 | return fileDescriptor_dfa3fe919df2773c, []int{0} 38 | } 39 | 40 | func (m *SubjectRequest) XXX_Unmarshal(b []byte) error { 41 | return xxx_messageInfo_SubjectRequest.Unmarshal(m, b) 42 | } 43 | func (m *SubjectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 44 | return xxx_messageInfo_SubjectRequest.Marshal(b, m, deterministic) 45 | } 46 | func (m *SubjectRequest) XXX_Merge(src proto.Message) { 47 | xxx_messageInfo_SubjectRequest.Merge(m, src) 48 | } 49 | func (m *SubjectRequest) XXX_Size() int { 50 | return xxx_messageInfo_SubjectRequest.Size(m) 51 | } 52 | func (m *SubjectRequest) XXX_DiscardUnknown() { 53 | xxx_messageInfo_SubjectRequest.DiscardUnknown(m) 54 | } 55 | 56 | var xxx_messageInfo_SubjectRequest proto.InternalMessageInfo 57 | 58 | func (m *SubjectRequest) GetMetadata() *Metadata { 59 | if m != nil { 60 | return m.Metadata 61 | } 62 | return nil 63 | } 64 | 65 | func (m *SubjectRequest) GetMessage() string { 66 | if m != nil { 67 | return m.Message 68 | } 69 | return "" 70 | } 71 | 72 | type SubjectResponse struct { 73 | Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` 74 | // response specific data 75 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 76 | Subjects []*Subject `protobuf:"bytes,3,rep,name=subjects,proto3" json:"subjects,omitempty"` 77 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 78 | XXX_unrecognized []byte `json:"-"` 79 | XXX_sizecache int32 `json:"-"` 80 | } 81 | 82 | func (m *SubjectResponse) Reset() { *m = SubjectResponse{} } 83 | func (m *SubjectResponse) String() string { return proto.CompactTextString(m) } 84 | func (*SubjectResponse) ProtoMessage() {} 85 | func (*SubjectResponse) Descriptor() ([]byte, []int) { 86 | return fileDescriptor_dfa3fe919df2773c, []int{1} 87 | } 88 | 89 | func (m *SubjectResponse) XXX_Unmarshal(b []byte) error { 90 | return xxx_messageInfo_SubjectResponse.Unmarshal(m, b) 91 | } 92 | func (m *SubjectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 93 | return xxx_messageInfo_SubjectResponse.Marshal(b, m, deterministic) 94 | } 95 | func (m *SubjectResponse) XXX_Merge(src proto.Message) { 96 | xxx_messageInfo_SubjectResponse.Merge(m, src) 97 | } 98 | func (m *SubjectResponse) XXX_Size() int { 99 | return xxx_messageInfo_SubjectResponse.Size(m) 100 | } 101 | func (m *SubjectResponse) XXX_DiscardUnknown() { 102 | xxx_messageInfo_SubjectResponse.DiscardUnknown(m) 103 | } 104 | 105 | var xxx_messageInfo_SubjectResponse proto.InternalMessageInfo 106 | 107 | func (m *SubjectResponse) GetMetadata() *Metadata { 108 | if m != nil { 109 | return m.Metadata 110 | } 111 | return nil 112 | } 113 | 114 | func (m *SubjectResponse) GetMessage() string { 115 | if m != nil { 116 | return m.Message 117 | } 118 | return "" 119 | } 120 | 121 | func (m *SubjectResponse) GetSubjects() []*Subject { 122 | if m != nil { 123 | return m.Subjects 124 | } 125 | return nil 126 | } 127 | 128 | type Subject struct { 129 | Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` 130 | Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` 131 | Proposer string `protobuf:"bytes,3,opt,name=proposer,proto3" json:"proposer,omitempty"` 132 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 133 | XXX_unrecognized []byte `json:"-"` 134 | XXX_sizecache int32 `json:"-"` 135 | } 136 | 137 | func (m *Subject) Reset() { *m = Subject{} } 138 | func (m *Subject) String() string { return proto.CompactTextString(m) } 139 | func (*Subject) ProtoMessage() {} 140 | func (*Subject) Descriptor() ([]byte, []int) { 141 | return fileDescriptor_dfa3fe919df2773c, []int{2} 142 | } 143 | 144 | func (m *Subject) XXX_Unmarshal(b []byte) error { 145 | return xxx_messageInfo_Subject.Unmarshal(m, b) 146 | } 147 | func (m *Subject) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 148 | return xxx_messageInfo_Subject.Marshal(b, m, deterministic) 149 | } 150 | func (m *Subject) XXX_Merge(src proto.Message) { 151 | xxx_messageInfo_Subject.Merge(m, src) 152 | } 153 | func (m *Subject) XXX_Size() int { 154 | return xxx_messageInfo_Subject.Size(m) 155 | } 156 | func (m *Subject) XXX_DiscardUnknown() { 157 | xxx_messageInfo_Subject.DiscardUnknown(m) 158 | } 159 | 160 | var xxx_messageInfo_Subject proto.InternalMessageInfo 161 | 162 | func (m *Subject) GetTitle() string { 163 | if m != nil { 164 | return m.Title 165 | } 166 | return "" 167 | } 168 | 169 | func (m *Subject) GetDescription() string { 170 | if m != nil { 171 | return m.Description 172 | } 173 | return "" 174 | } 175 | 176 | func (m *Subject) GetProposer() string { 177 | if m != nil { 178 | return m.Proposer 179 | } 180 | return "" 181 | } 182 | 183 | type IdentityRequest struct { 184 | Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` 185 | // method specific data 186 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 187 | SubjectHash []byte `protobuf:"bytes,3,opt,name=subjectHash,proto3" json:"subjectHash,omitempty"` 188 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 189 | XXX_unrecognized []byte `json:"-"` 190 | XXX_sizecache int32 `json:"-"` 191 | } 192 | 193 | func (m *IdentityRequest) Reset() { *m = IdentityRequest{} } 194 | func (m *IdentityRequest) String() string { return proto.CompactTextString(m) } 195 | func (*IdentityRequest) ProtoMessage() {} 196 | func (*IdentityRequest) Descriptor() ([]byte, []int) { 197 | return fileDescriptor_dfa3fe919df2773c, []int{3} 198 | } 199 | 200 | func (m *IdentityRequest) XXX_Unmarshal(b []byte) error { 201 | return xxx_messageInfo_IdentityRequest.Unmarshal(m, b) 202 | } 203 | func (m *IdentityRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 204 | return xxx_messageInfo_IdentityRequest.Marshal(b, m, deterministic) 205 | } 206 | func (m *IdentityRequest) XXX_Merge(src proto.Message) { 207 | xxx_messageInfo_IdentityRequest.Merge(m, src) 208 | } 209 | func (m *IdentityRequest) XXX_Size() int { 210 | return xxx_messageInfo_IdentityRequest.Size(m) 211 | } 212 | func (m *IdentityRequest) XXX_DiscardUnknown() { 213 | xxx_messageInfo_IdentityRequest.DiscardUnknown(m) 214 | } 215 | 216 | var xxx_messageInfo_IdentityRequest proto.InternalMessageInfo 217 | 218 | func (m *IdentityRequest) GetMetadata() *Metadata { 219 | if m != nil { 220 | return m.Metadata 221 | } 222 | return nil 223 | } 224 | 225 | func (m *IdentityRequest) GetMessage() string { 226 | if m != nil { 227 | return m.Message 228 | } 229 | return "" 230 | } 231 | 232 | func (m *IdentityRequest) GetSubjectHash() []byte { 233 | if m != nil { 234 | return m.SubjectHash 235 | } 236 | return nil 237 | } 238 | 239 | type IdentityResponse struct { 240 | Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` 241 | // response specific data 242 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 243 | SubjectHash []byte `protobuf:"bytes,3,opt,name=subjectHash,proto3" json:"subjectHash,omitempty"` 244 | IdentitySet []string `protobuf:"bytes,4,rep,name=identitySet,proto3" json:"identitySet,omitempty"` 245 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 246 | XXX_unrecognized []byte `json:"-"` 247 | XXX_sizecache int32 `json:"-"` 248 | } 249 | 250 | func (m *IdentityResponse) Reset() { *m = IdentityResponse{} } 251 | func (m *IdentityResponse) String() string { return proto.CompactTextString(m) } 252 | func (*IdentityResponse) ProtoMessage() {} 253 | func (*IdentityResponse) Descriptor() ([]byte, []int) { 254 | return fileDescriptor_dfa3fe919df2773c, []int{4} 255 | } 256 | 257 | func (m *IdentityResponse) XXX_Unmarshal(b []byte) error { 258 | return xxx_messageInfo_IdentityResponse.Unmarshal(m, b) 259 | } 260 | func (m *IdentityResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 261 | return xxx_messageInfo_IdentityResponse.Marshal(b, m, deterministic) 262 | } 263 | func (m *IdentityResponse) XXX_Merge(src proto.Message) { 264 | xxx_messageInfo_IdentityResponse.Merge(m, src) 265 | } 266 | func (m *IdentityResponse) XXX_Size() int { 267 | return xxx_messageInfo_IdentityResponse.Size(m) 268 | } 269 | func (m *IdentityResponse) XXX_DiscardUnknown() { 270 | xxx_messageInfo_IdentityResponse.DiscardUnknown(m) 271 | } 272 | 273 | var xxx_messageInfo_IdentityResponse proto.InternalMessageInfo 274 | 275 | func (m *IdentityResponse) GetMetadata() *Metadata { 276 | if m != nil { 277 | return m.Metadata 278 | } 279 | return nil 280 | } 281 | 282 | func (m *IdentityResponse) GetMessage() string { 283 | if m != nil { 284 | return m.Message 285 | } 286 | return "" 287 | } 288 | 289 | func (m *IdentityResponse) GetSubjectHash() []byte { 290 | if m != nil { 291 | return m.SubjectHash 292 | } 293 | return nil 294 | } 295 | 296 | func (m *IdentityResponse) GetIdentitySet() []string { 297 | if m != nil { 298 | return m.IdentitySet 299 | } 300 | return nil 301 | } 302 | 303 | type BallotRequest struct { 304 | Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` 305 | // method specific data 306 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 307 | SubjectHash []byte `protobuf:"bytes,3,opt,name=subjectHash,proto3" json:"subjectHash,omitempty"` 308 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 309 | XXX_unrecognized []byte `json:"-"` 310 | XXX_sizecache int32 `json:"-"` 311 | } 312 | 313 | func (m *BallotRequest) Reset() { *m = BallotRequest{} } 314 | func (m *BallotRequest) String() string { return proto.CompactTextString(m) } 315 | func (*BallotRequest) ProtoMessage() {} 316 | func (*BallotRequest) Descriptor() ([]byte, []int) { 317 | return fileDescriptor_dfa3fe919df2773c, []int{5} 318 | } 319 | 320 | func (m *BallotRequest) XXX_Unmarshal(b []byte) error { 321 | return xxx_messageInfo_BallotRequest.Unmarshal(m, b) 322 | } 323 | func (m *BallotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 324 | return xxx_messageInfo_BallotRequest.Marshal(b, m, deterministic) 325 | } 326 | func (m *BallotRequest) XXX_Merge(src proto.Message) { 327 | xxx_messageInfo_BallotRequest.Merge(m, src) 328 | } 329 | func (m *BallotRequest) XXX_Size() int { 330 | return xxx_messageInfo_BallotRequest.Size(m) 331 | } 332 | func (m *BallotRequest) XXX_DiscardUnknown() { 333 | xxx_messageInfo_BallotRequest.DiscardUnknown(m) 334 | } 335 | 336 | var xxx_messageInfo_BallotRequest proto.InternalMessageInfo 337 | 338 | func (m *BallotRequest) GetMetadata() *Metadata { 339 | if m != nil { 340 | return m.Metadata 341 | } 342 | return nil 343 | } 344 | 345 | func (m *BallotRequest) GetMessage() string { 346 | if m != nil { 347 | return m.Message 348 | } 349 | return "" 350 | } 351 | 352 | func (m *BallotRequest) GetSubjectHash() []byte { 353 | if m != nil { 354 | return m.SubjectHash 355 | } 356 | return nil 357 | } 358 | 359 | type BallotResponse struct { 360 | Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` 361 | // response specific data 362 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 363 | SubjectHash []byte `protobuf:"bytes,3,opt,name=subjectHash,proto3" json:"subjectHash,omitempty"` 364 | BallotSet []string `protobuf:"bytes,4,rep,name=ballotSet,proto3" json:"ballotSet,omitempty"` 365 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 366 | XXX_unrecognized []byte `json:"-"` 367 | XXX_sizecache int32 `json:"-"` 368 | } 369 | 370 | func (m *BallotResponse) Reset() { *m = BallotResponse{} } 371 | func (m *BallotResponse) String() string { return proto.CompactTextString(m) } 372 | func (*BallotResponse) ProtoMessage() {} 373 | func (*BallotResponse) Descriptor() ([]byte, []int) { 374 | return fileDescriptor_dfa3fe919df2773c, []int{6} 375 | } 376 | 377 | func (m *BallotResponse) XXX_Unmarshal(b []byte) error { 378 | return xxx_messageInfo_BallotResponse.Unmarshal(m, b) 379 | } 380 | func (m *BallotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 381 | return xxx_messageInfo_BallotResponse.Marshal(b, m, deterministic) 382 | } 383 | func (m *BallotResponse) XXX_Merge(src proto.Message) { 384 | xxx_messageInfo_BallotResponse.Merge(m, src) 385 | } 386 | func (m *BallotResponse) XXX_Size() int { 387 | return xxx_messageInfo_BallotResponse.Size(m) 388 | } 389 | func (m *BallotResponse) XXX_DiscardUnknown() { 390 | xxx_messageInfo_BallotResponse.DiscardUnknown(m) 391 | } 392 | 393 | var xxx_messageInfo_BallotResponse proto.InternalMessageInfo 394 | 395 | func (m *BallotResponse) GetMetadata() *Metadata { 396 | if m != nil { 397 | return m.Metadata 398 | } 399 | return nil 400 | } 401 | 402 | func (m *BallotResponse) GetMessage() string { 403 | if m != nil { 404 | return m.Message 405 | } 406 | return "" 407 | } 408 | 409 | func (m *BallotResponse) GetSubjectHash() []byte { 410 | if m != nil { 411 | return m.SubjectHash 412 | } 413 | return nil 414 | } 415 | 416 | func (m *BallotResponse) GetBallotSet() []string { 417 | if m != nil { 418 | return m.BallotSet 419 | } 420 | return nil 421 | } 422 | 423 | // designed to be shared between all app protocols 424 | type Metadata struct { 425 | // shared between all requests 426 | ClientVersion string `protobuf:"bytes,1,opt,name=clientVersion,proto3" json:"clientVersion,omitempty"` 427 | Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` 428 | Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` 429 | Gossip bool `protobuf:"varint,4,opt,name=gossip,proto3" json:"gossip,omitempty"` 430 | NodeId string `protobuf:"bytes,5,opt,name=nodeId,proto3" json:"nodeId,omitempty"` 431 | NodePubKey []byte `protobuf:"bytes,6,opt,name=nodePubKey,proto3" json:"nodePubKey,omitempty"` 432 | Sign []byte `protobuf:"bytes,7,opt,name=sign,proto3" json:"sign,omitempty"` 433 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 434 | XXX_unrecognized []byte `json:"-"` 435 | XXX_sizecache int32 `json:"-"` 436 | } 437 | 438 | func (m *Metadata) Reset() { *m = Metadata{} } 439 | func (m *Metadata) String() string { return proto.CompactTextString(m) } 440 | func (*Metadata) ProtoMessage() {} 441 | func (*Metadata) Descriptor() ([]byte, []int) { 442 | return fileDescriptor_dfa3fe919df2773c, []int{7} 443 | } 444 | 445 | func (m *Metadata) XXX_Unmarshal(b []byte) error { 446 | return xxx_messageInfo_Metadata.Unmarshal(m, b) 447 | } 448 | func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 449 | return xxx_messageInfo_Metadata.Marshal(b, m, deterministic) 450 | } 451 | func (m *Metadata) XXX_Merge(src proto.Message) { 452 | xxx_messageInfo_Metadata.Merge(m, src) 453 | } 454 | func (m *Metadata) XXX_Size() int { 455 | return xxx_messageInfo_Metadata.Size(m) 456 | } 457 | func (m *Metadata) XXX_DiscardUnknown() { 458 | xxx_messageInfo_Metadata.DiscardUnknown(m) 459 | } 460 | 461 | var xxx_messageInfo_Metadata proto.InternalMessageInfo 462 | 463 | func (m *Metadata) GetClientVersion() string { 464 | if m != nil { 465 | return m.ClientVersion 466 | } 467 | return "" 468 | } 469 | 470 | func (m *Metadata) GetTimestamp() int64 { 471 | if m != nil { 472 | return m.Timestamp 473 | } 474 | return 0 475 | } 476 | 477 | func (m *Metadata) GetId() string { 478 | if m != nil { 479 | return m.Id 480 | } 481 | return "" 482 | } 483 | 484 | func (m *Metadata) GetGossip() bool { 485 | if m != nil { 486 | return m.Gossip 487 | } 488 | return false 489 | } 490 | 491 | func (m *Metadata) GetNodeId() string { 492 | if m != nil { 493 | return m.NodeId 494 | } 495 | return "" 496 | } 497 | 498 | func (m *Metadata) GetNodePubKey() []byte { 499 | if m != nil { 500 | return m.NodePubKey 501 | } 502 | return nil 503 | } 504 | 505 | func (m *Metadata) GetSign() []byte { 506 | if m != nil { 507 | return m.Sign 508 | } 509 | return nil 510 | } 511 | 512 | func init() { 513 | proto.RegisterType((*SubjectRequest)(nil), "protocols.zkvote.SubjectRequest") 514 | proto.RegisterType((*SubjectResponse)(nil), "protocols.zkvote.SubjectResponse") 515 | proto.RegisterType((*Subject)(nil), "protocols.zkvote.Subject") 516 | proto.RegisterType((*IdentityRequest)(nil), "protocols.zkvote.IdentityRequest") 517 | proto.RegisterType((*IdentityResponse)(nil), "protocols.zkvote.IdentityResponse") 518 | proto.RegisterType((*BallotRequest)(nil), "protocols.zkvote.BallotRequest") 519 | proto.RegisterType((*BallotResponse)(nil), "protocols.zkvote.BallotResponse") 520 | proto.RegisterType((*Metadata)(nil), "protocols.zkvote.Metadata") 521 | } 522 | 523 | func init() { proto.RegisterFile("zkvote.proto", fileDescriptor_dfa3fe919df2773c) } 524 | 525 | var fileDescriptor_dfa3fe919df2773c = []byte{ 526 | // 408 bytes of a gzipped FileDescriptorProto 527 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x92, 0xcf, 0x8a, 0xd4, 0x40, 528 | 0x10, 0xc6, 0xe9, 0xc9, 0xec, 0x4c, 0xa6, 0x66, 0x77, 0x76, 0x69, 0x44, 0xda, 0x45, 0x24, 0x04, 529 | 0x0f, 0x39, 0xe5, 0xb0, 0xa2, 0x0f, 0xe0, 0xc9, 0x45, 0x04, 0xc9, 0x82, 0x37, 0x0f, 0xf9, 0x53, 530 | 0x8c, 0xad, 0x49, 0x3a, 0xa6, 0x6a, 0x84, 0xf5, 0x2a, 0xbe, 0x84, 0x47, 0x4f, 0x3e, 0x87, 0x4f, 531 | 0x26, 0xe9, 0x74, 0xfe, 0xa8, 0x07, 0x2f, 0x2e, 0x73, 0x4a, 0xd7, 0xd7, 0x95, 0xfe, 0x7e, 0xfd, 532 | 0x75, 0xc1, 0xe9, 0xe7, 0x0f, 0x9f, 0x0c, 0x63, 0xdc, 0xb4, 0x86, 0x8d, 0xbc, 0xb0, 0x9f, 0xdc, 533 | 0x94, 0x14, 0xf7, 0x7a, 0x98, 0xc1, 0xee, 0xe6, 0x90, 0xbd, 0xc7, 0x9c, 0x13, 0xfc, 0x78, 0x40, 534 | 0x62, 0xf9, 0x0c, 0xfc, 0x0a, 0x39, 0x2d, 0x52, 0x4e, 0x95, 0x08, 0x44, 0xb4, 0xbd, 0xba, 0x8c, 535 | 0xff, 0xfc, 0x2d, 0x7e, 0xe5, 0x3a, 0x92, 0xb1, 0x57, 0x2a, 0x58, 0x57, 0x48, 0x94, 0xee, 0x51, 536 | 0x2d, 0x02, 0x11, 0x6d, 0x92, 0xa1, 0x0c, 0xbf, 0x09, 0x38, 0x1f, 0x4d, 0xa8, 0x31, 0x35, 0xe1, 537 | 0xff, 0x77, 0x91, 0x4f, 0xc1, 0xa7, 0xde, 0x84, 0x94, 0x17, 0x78, 0xd1, 0xf6, 0xea, 0xc1, 0xdf, 538 | 0x27, 0x0e, 0x18, 0x63, 0x6b, 0xf8, 0x16, 0xd6, 0x4e, 0x94, 0xf7, 0xe0, 0x84, 0x35, 0x97, 0x68, 539 | 0x81, 0x36, 0x49, 0x5f, 0xc8, 0x00, 0xb6, 0x05, 0x52, 0xde, 0xea, 0x86, 0xb5, 0xa9, 0x9d, 0xeb, 540 | 0x5c, 0x92, 0x97, 0xe0, 0x37, 0xad, 0x69, 0x0c, 0x61, 0xab, 0x3c, 0xbb, 0x3d, 0xd6, 0xe1, 0x57, 541 | 0x01, 0xe7, 0xd7, 0x05, 0xd6, 0xac, 0xf9, 0xf6, 0xce, 0x12, 0xee, 0x18, 0xdd, 0x85, 0x5e, 0xa4, 542 | 0xf4, 0xce, 0x42, 0x9c, 0x26, 0x73, 0x29, 0xfc, 0x21, 0xe0, 0x62, 0xe2, 0xb8, 0xb3, 0x47, 0xf8, 543 | 0x27, 0x48, 0xd7, 0xa1, 0x1d, 0xc7, 0x0d, 0xb2, 0x5a, 0x06, 0x5e, 0x17, 0xe7, 0x4c, 0x0a, 0xbf, 544 | 0x08, 0x38, 0x7b, 0x9e, 0x96, 0xa5, 0xe1, 0x63, 0x06, 0xf6, 0x5d, 0xc0, 0x6e, 0xa0, 0x38, 0x62, 545 | 0x5c, 0x0f, 0x61, 0x93, 0x59, 0x8a, 0x29, 0xac, 0x49, 0x08, 0x7f, 0x0a, 0xf0, 0x07, 0x43, 0xf9, 546 | 0x18, 0xce, 0xf2, 0x52, 0x63, 0xcd, 0x6f, 0xb0, 0xa5, 0x6e, 0x54, 0xfb, 0x31, 0xfe, 0x5d, 0xec, 547 | 0x0e, 0x64, 0x5d, 0x21, 0x71, 0x5a, 0x35, 0x16, 0xc7, 0x4b, 0x26, 0x41, 0xee, 0x60, 0xa1, 0x0b, 548 | 0x37, 0xc4, 0x0b, 0x5d, 0xc8, 0xfb, 0xb0, 0xda, 0x1b, 0x22, 0xdd, 0xa8, 0x65, 0x20, 0x22, 0x3f, 549 | 0x71, 0x55, 0xa7, 0xd7, 0xa6, 0xc0, 0xeb, 0x42, 0x9d, 0xd8, 0x5e, 0x57, 0xc9, 0x47, 0x00, 0xdd, 550 | 0xea, 0xf5, 0x21, 0x7b, 0x89, 0xb7, 0x6a, 0x65, 0xef, 0x33, 0x53, 0xa4, 0x84, 0x25, 0xe9, 0x7d, 551 | 0xad, 0xd6, 0x76, 0xc7, 0xae, 0xb3, 0x95, 0xcd, 0xf0, 0xc9, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 552 | 0x4e, 0xf2, 0xfc, 0x81, 0xab, 0x04, 0x00, 0x00, 553 | } 554 | -------------------------------------------------------------------------------- /zkvote/model/pb/zkvote.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocols.zkvote; 4 | 5 | // subject protocol 6 | 7 | // a protocol define a set of reuqest and responses 8 | message SubjectRequest { 9 | Metadata metadata = 1; 10 | 11 | // method specific data 12 | string message = 2; 13 | } 14 | 15 | message SubjectResponse { 16 | Metadata metadata = 1; 17 | 18 | // response specific data 19 | string message = 2; 20 | repeated Subject subjects = 3; 21 | } 22 | 23 | message Subject { 24 | string title = 1; 25 | string description = 2; 26 | string proposer = 3; 27 | } 28 | 29 | // identity protocol 30 | 31 | message IdentityRequest { 32 | Metadata metadata = 1; 33 | 34 | // method specific data 35 | string message = 2; 36 | bytes subjectHash = 3; 37 | } 38 | 39 | message IdentityResponse { 40 | Metadata metadata = 1; 41 | 42 | // response specific data 43 | string message = 2; 44 | bytes subjectHash = 3; 45 | repeated string identitySet = 4; 46 | } 47 | 48 | message BallotRequest { 49 | Metadata metadata = 1; 50 | 51 | // method specific data 52 | string message = 2; 53 | bytes subjectHash = 3; 54 | } 55 | 56 | message BallotResponse { 57 | Metadata metadata = 1; 58 | 59 | // response specific data 60 | string message = 2; 61 | bytes subjectHash = 3; 62 | repeated string ballotSet = 4; 63 | } 64 | 65 | // designed to be shared between all app protocols 66 | message Metadata { 67 | // shared between all requests 68 | string clientVersion = 1; // client version 69 | int64 timestamp = 2; // unix time 70 | string id = 3; // allows requesters to use request data when processing a response 71 | bool gossip = 4; // true to have receiver peer gossip the message to neighbors 72 | string nodeId = 5; // id of node that created the message (not the peer that may have sent it). =base58(multihash(nodePubKey)) 73 | bytes nodePubKey = 6; // Authoring node Secp256k1 public key (32bytes) - protobufs serielized 74 | bytes sign = 7; // signature of message data + method specific data by message authoring node. 75 | } 76 | -------------------------------------------------------------------------------- /zkvote/model/subject/subject.go: -------------------------------------------------------------------------------- 1 | package subject 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | 7 | "github.com/unitychain/zkvote-node/zkvote/model/identity" 8 | ) 9 | 10 | // Subject ... 11 | type Subject struct { 12 | Title string `json:"title"` 13 | Description string `json:"Desc"` 14 | Proposer *identity.Identity `json:"proposer"` 15 | hash HashHex 16 | } 17 | 18 | // Hash ... 19 | type Hash []byte 20 | 21 | // Byte ... 22 | func (h Hash) Byte() []byte { return []byte(h) } 23 | 24 | // Hex ... 25 | func (h Hash) Hex() HashHex { 26 | return HashHex(hex.EncodeToString(h.Byte())) 27 | } 28 | 29 | // Map ... 30 | type Map map[HashHex]*Subject 31 | 32 | // HashHex ... 33 | type HashHex string 34 | 35 | // String ... 36 | func (h HashHex) String() string { return string(h) } 37 | 38 | // Hash ... 39 | func (h HashHex) Hash() Hash { 40 | result, _ := hex.DecodeString(string(h)) 41 | return Hash(result) 42 | } 43 | 44 | // NewSubject ... 45 | func NewSubject(title string, description string, identity *identity.Identity) *Subject { 46 | s := Subject{Title: title, Description: description, Proposer: identity} 47 | s.hash = s.Hash().Hex() 48 | return &s 49 | } 50 | 51 | // NewMap ... 52 | func NewMap() Map { 53 | return Map(make(map[HashHex]*Subject)) 54 | } 55 | 56 | // HashHex . 57 | func (s *Subject) HashHex() *HashHex { 58 | if 0 == len(s.hash) { 59 | s.hash = s.Hash().Hex() 60 | } 61 | return &s.hash 62 | } 63 | 64 | // Hash ... 65 | func (s *Subject) Hash() *Hash { 66 | h := sha256.Sum256([]byte(s.Title + s.Description + s.Proposer.String())) 67 | result := Hash(h[:]) 68 | return &result 69 | } 70 | 71 | // JSON ... 72 | func (s *Subject) JSON() map[string]string { 73 | return map[string]string{ 74 | "hash": s.HashHex().String(), 75 | "title": s.Title, 76 | "description": s.Description, 77 | "proposer": s.Proposer.String(), 78 | } 79 | } 80 | 81 | // GetTitle ... 82 | func (s *Subject) GetTitle() string { 83 | return s.Title 84 | } 85 | 86 | // GetDescription ... 87 | func (s *Subject) GetDescription() string { 88 | return s.Description 89 | } 90 | 91 | // GetProposer ... 92 | func (s *Subject) GetProposer() *identity.Identity { 93 | return s.Proposer 94 | } 95 | -------------------------------------------------------------------------------- /zkvote/node/node.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/ipfs/go-datastore" 9 | "github.com/libp2p/go-libp2p" 10 | connmgr "github.com/libp2p/go-libp2p-connmgr" 11 | crypto "github.com/libp2p/go-libp2p-crypto" 12 | dht "github.com/libp2p/go-libp2p-kad-dht" 13 | dhtopts "github.com/libp2p/go-libp2p-kad-dht/opts" 14 | "github.com/unitychain/zkvote-node/zkvote/common/store" 15 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 16 | zkp "github.com/unitychain/zkvote-node/zkvote/node/zkp_vote" 17 | ) 18 | 19 | const DB_NODE_ID = "nodeID" 20 | 21 | type Node struct { 22 | zkpVote *zkp.ZkpVote 23 | store *store.Store 24 | } 25 | 26 | func NewNode(ctx context.Context, ds datastore.Batching, bucketSize int) *Node { 27 | cmgr := connmgr.NewConnManager(1500, 2000, time.Minute) 28 | 29 | // Ignoring most errors for brevity 30 | // See echo example for more details and better implementation 31 | prvKey, err := loadPrivateKey(ds) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | opts := []libp2p.Option{libp2p.ConnectionManager(cmgr), libp2p.Identity(prvKey)} 37 | host, err := libp2p.New(context.Background(), opts...) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | d1, err := dht.New(context.Background(), host, dhtopts.BucketSize(bucketSize), dhtopts.Datastore(ds), dhtopts.Validator(store.NodeValidator{})) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | store, err := store.NewStore(d1, ds) 48 | if err != nil { 49 | utils.LogFatalf("New store error, %v", err.Error()) 50 | } 51 | 52 | zkp, err := zkp.NewZkpVote(store) 53 | if err != nil { 54 | utils.LogFatalf("New ZKP Vote error, %v", err.Error()) 55 | } 56 | 57 | return &Node{ 58 | zkpVote: zkp, 59 | store: store, 60 | } 61 | } 62 | 63 | func loadPrivateKey(ds datastore.Batching) (crypto.PrivKey, error) { 64 | var prvKey crypto.PrivKey 65 | var err error 66 | 67 | tmpStore, _ := store.NewStore(nil, ds) 68 | strKey, _ := tmpStore.GetLocal(DB_NODE_ID) 69 | if 0 == len(strKey) { 70 | utils.LogInfof("Generate a new private key!") 71 | prvKey, _, _ = crypto.GenerateKeyPair(crypto.ECDSA, 0) 72 | // priv, _, _ := crypto.GenerateKeyPair(crypto.Ed25519, 0) 73 | b, _ := prvKey.Raw() 74 | tmpStore.PutLocal(DB_NODE_ID, utils.Remove0x(utils.GetHexStringFromBytes(b))) 75 | } else { 76 | prvKey, err = crypto.UnmarshalECDSAPrivateKey(utils.GetBytesFromHexString(strKey)) 77 | if err != nil { 78 | return nil, fmt.Errorf("unmarshal private key error, %v", err) 79 | } 80 | } 81 | b, _ := prvKey.GetPublic().Bytes() 82 | utils.LogInfof("peer pub key: %v", utils.GetHexStringFromBytes(b)) 83 | 84 | return prvKey, err 85 | } 86 | -------------------------------------------------------------------------------- /zkvote/node/zkp_vote/votes.go: -------------------------------------------------------------------------------- 1 | package zkp_vote 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/big" 7 | "strings" 8 | 9 | "github.com/ethereum/go-ethereum/crypto" 10 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 11 | tree "github.com/unitychain/zkvote-node/zkvote/model/identity" 12 | s "github.com/unitychain/zkvote-node/zkvote/model/subject" 13 | ) 14 | 15 | type VoteLeaf struct { 16 | Subject *s.Subject `json:"subject"` 17 | Ballots []int `json:"ballots"` 18 | Hash s.HashHex `json:"hash"` 19 | } 20 | 21 | type Votes struct { 22 | VoteLeaves []*VoteLeaf `json:"VoteLeaves"` 23 | Root *big.Int `json:"root"` 24 | voteTree *tree.MerkleTree 25 | } 26 | 27 | func NewVotes() (*Votes, error) { 28 | return &Votes{ 29 | VoteLeaves: []*VoteLeaf{ 30 | &VoteLeaf{ 31 | Subject: nil, 32 | Ballots: []int{0, 0}, 33 | Hash: s.HashHex(""), 34 | }}, 35 | Root: big.NewInt(0), 36 | }, nil 37 | } 38 | 39 | func NewVotesWithSerializedString(jsonStr string) (*Votes, error) { 40 | var v Votes 41 | err := json.Unmarshal([]byte(jsonStr), &v) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return &v, nil 46 | } 47 | 48 | func (v *Votes) Serialize() (string, error) { 49 | jsonStrB, err := json.Marshal(v) 50 | if err != nil { 51 | utils.LogErrorf("Marshal error %v", err.Error()) 52 | return "", err 53 | } 54 | return string(jsonStrB), nil 55 | } 56 | 57 | func (v *Votes) CreateAVote(subject *s.Subject) error { 58 | // Or prevent from the same title 59 | leaf := v.getVoteLeaf(*subject.HashHex()) 60 | if leaf != nil { 61 | return fmt.Errorf("Subject is existed, hash:%v", subject.HashHex()) 62 | } 63 | 64 | l := &VoteLeaf{ 65 | Subject: subject, 66 | Ballots: []int{0, 0}, 67 | Hash: *subject.HashHex(), 68 | } 69 | return v.insertLeaf(l) 70 | } 71 | 72 | func (v *Votes) Update(subHashHex s.HashHex, newBallot []int) error { 73 | leaf := v.getVoteLeaf(subHashHex) 74 | if leaf == nil { 75 | return fmt.Errorf("Can't find this subject") 76 | } 77 | leaf.Ballots[0] = newBallot[0] 78 | leaf.Ballots[1] = newBallot[1] 79 | 80 | v.Root = v.calcRoot() 81 | return nil 82 | } 83 | 84 | func (v *Votes) IsValidBallotNumber(subHashHex s.HashHex, newBallot []int) (bool, error) { 85 | leaf := v.getVoteLeaf(subHashHex) 86 | if leaf == nil { 87 | return false, fmt.Errorf("Can't find this subject") 88 | } 89 | 90 | if leaf.Ballots[0] > newBallot[0] || leaf.Ballots[1] > newBallot[1] { 91 | return false, fmt.Errorf("number of coming ballots is wrong, current:%v, coming:%v", leaf.Ballots, newBallot) 92 | } 93 | 94 | return true, nil 95 | } 96 | 97 | func (v *Votes) IsRootMatched(root *big.Int) bool { 98 | if 0 == v.Root.Cmp(root) { 99 | return true 100 | } 101 | return false 102 | } 103 | 104 | // 105 | // Internals 106 | // 107 | func (v *Votes) calcRoot() *big.Int { 108 | return v.voteTree.GetRoot().BigInt() 109 | } 110 | 111 | func (v *Votes) getVoteLeaf(subHashHex s.HashHex) *VoteLeaf { 112 | for _, l := range v.VoteLeaves { 113 | if strings.EqualFold(utils.Prepend0x(subHashHex.String()), l.Hash.String()) { 114 | return l 115 | } 116 | } 117 | utils.LogWarningf("Can't find subject hash, %v", subHashHex) 118 | return nil 119 | } 120 | 121 | func (v *Votes) insertLeaf(leaf *VoteLeaf) error { 122 | 123 | b, e := json.Marshal(v.VoteLeaves) 124 | if e != nil { 125 | return e 126 | } 127 | 128 | x := big.NewInt(0).SetBytes(crypto.Keccak256(b)) 129 | _, e = v.voteTree.Insert(*tree.NewTreeContent(x)) 130 | if e != nil { 131 | return e 132 | } 133 | 134 | v.VoteLeaves = append(v.VoteLeaves, leaf) 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /zkvote/node/zkp_vote/zkp_vote.go: -------------------------------------------------------------------------------- 1 | package zkp_vote 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "strconv" 7 | 8 | "github.com/arnaucube/go-snark/externalVerif" 9 | "github.com/unitychain/zkvote-node/zkvote/common/store" 10 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 11 | "github.com/unitychain/zkvote-node/zkvote/snark" 12 | 13 | s "github.com/unitychain/zkvote-node/zkvote/model/subject" 14 | ) 15 | 16 | const ZKPVOTE_DHT_KEY = "zkp-votes" 17 | 18 | type RollupProof struct { 19 | Root string `json:"root"` 20 | Ballots []int `json:"ballots"` 21 | Proof *externalVerif.CircomProof `json:"proof"` 22 | PublicSignal []string `json:"public_signal"` 23 | } 24 | 25 | type ZkpVote struct { 26 | votes *Votes 27 | } 28 | 29 | func NewZkpVote(s *store.Store) (*ZkpVote, error) { 30 | 31 | v, err := loadDataFromDHT(s) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return &ZkpVote{ 37 | votes: v, 38 | }, nil 39 | } 40 | 41 | func loadDataFromDHT(s *store.Store) (*Votes, error) { 42 | utils.LogDebugf("ZKPVote: load DHT") 43 | 44 | value, err := s.GetDHT(ZKPVOTE_DHT_KEY) 45 | if err != nil { 46 | utils.LogErrorf("Get DHT data error, %v", err) 47 | return nil, err 48 | } 49 | 50 | if len(value) == 0 { 51 | return NewVotes() 52 | } 53 | return NewVotesWithSerializedString(string(value)) 54 | } 55 | 56 | func (z *ZkpVote) Rollup(subHashHex s.HashHex, prvRoot *big.Int, rProof *RollupProof, vkString string) error { 57 | if !z.votes.IsRootMatched(prvRoot) { 58 | utils.LogErrorf("Not match current root %v/%v", z.votes.Root, prvRoot) 59 | return fmt.Errorf("Not match current root %v/%v", z.votes.Root, prvRoot) 60 | } 61 | if b, e := z.isValidProof(subHashHex, rProof, vkString); !b { 62 | utils.LogErrorf("Not a valid proof, ", e) 63 | return e 64 | } 65 | 66 | ballot_Yes, _ := strconv.Atoi(rProof.PublicSignal[1]) 67 | ballot_No, _ := strconv.Atoi(rProof.PublicSignal[2]) 68 | return z.votes.Update(subHashHex, []int{ballot_Yes, ballot_No}) 69 | } 70 | 71 | func (z *ZkpVote) isValidProof(subj s.HashHex, rProof *RollupProof, vkString string) (bool, error) { 72 | if 0 == len(vkString) { 73 | utils.LogWarningf("invalid input: %s", vkString) 74 | return false, fmt.Errorf("vk string is empty") 75 | } 76 | 77 | newRoot := rProof.PublicSignal[0] 78 | ballot_Yes, _ := strconv.Atoi(rProof.PublicSignal[1]) 79 | ballot_No, _ := strconv.Atoi(rProof.PublicSignal[2]) 80 | 81 | bigNewRoot, _ := big.NewInt(0).SetString(newRoot, 10) 82 | bigRoot, _ := big.NewInt(0).SetString(rProof.Root, 10) 83 | if 0 != bigRoot.Cmp(bigNewRoot) { 84 | utils.LogWarningf("root is inconsistent (%v)/(%v)", rProof.Root, bigNewRoot) 85 | return false, fmt.Errorf(fmt.Sprintf("root is inconsistent (%v)/(%v)", rProof.Root, bigNewRoot)) 86 | } 87 | 88 | if b, e := z.votes.IsValidBallotNumber(subj, []int{ballot_Yes, ballot_No}); !b { 89 | utils.LogWarningf("Not a valid ballot, %v", e) 90 | return false, e 91 | } 92 | 93 | return snark.Verify(vkString, rProof.Proof, rProof.PublicSignal), nil 94 | } 95 | -------------------------------------------------------------------------------- /zkvote/operator/operator.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io/ioutil" 7 | "sync" 8 | "time" 9 | 10 | "github.com/ipfs/go-datastore" 11 | ipns "github.com/ipfs/go-ipns" 12 | 13 | "github.com/libp2p/go-libp2p" 14 | circuit "github.com/libp2p/go-libp2p-circuit" 15 | connmgr "github.com/libp2p/go-libp2p-connmgr" 16 | crypto "github.com/libp2p/go-libp2p-core/crypto" 17 | "github.com/libp2p/go-libp2p-core/network" 18 | "github.com/libp2p/go-libp2p-core/peer" 19 | dht "github.com/libp2p/go-libp2p-kad-dht" 20 | dhtopts "github.com/libp2p/go-libp2p-kad-dht/opts" 21 | pubsub "github.com/libp2p/go-libp2p-pubsub" 22 | record "github.com/libp2p/go-libp2p-record" 23 | msdnDiscovery "github.com/libp2p/go-libp2p/p2p/discovery" 24 | 25 | "github.com/manifoldco/promptui" 26 | ma "github.com/multiformats/go-multiaddr" 27 | "github.com/unitychain/zkvote-node/zkvote/common/store" 28 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 29 | localContext "github.com/unitychain/zkvote-node/zkvote/model/context" 30 | "github.com/unitychain/zkvote-node/zkvote/operator/service/manager" 31 | ) 32 | 33 | // node client version 34 | 35 | const DB_PEER_ID = "peerID" 36 | 37 | // Node ... 38 | type Operator struct { 39 | *localContext.Context 40 | *manager.Manager 41 | dht *dht.IpfsDHT 42 | pubsub *pubsub.PubSub 43 | db datastore.Batching 44 | mdnsPeers map[peer.ID]peer.AddrInfo 45 | streams chan network.Stream 46 | } 47 | 48 | func loadPrivateKey(ds datastore.Batching) (crypto.PrivKey, error) { 49 | var prvKey crypto.PrivKey 50 | var err error 51 | 52 | tmpStore, _ := store.NewStore(nil, ds) 53 | strKey, _ := tmpStore.GetLocal(DB_PEER_ID) 54 | if 0 == len(strKey) { 55 | utils.LogInfof("Generate a new private key!") 56 | prvKey, _, _ = crypto.GenerateKeyPair(crypto.ECDSA, 0) 57 | // priv, _, _ := crypto.GenerateKeyPair(crypto.Ed25519, 0) 58 | b, _ := prvKey.Raw() 59 | tmpStore.PutLocal(DB_PEER_ID, utils.Remove0x(utils.GetHexStringFromBytes(b))) 60 | } else { 61 | prvKey, err = crypto.UnmarshalECDSAPrivateKey(utils.GetBytesFromHexString(strKey)) 62 | if err != nil { 63 | return nil, fmt.Errorf("unmarshal private key error, %v", err) 64 | } 65 | } 66 | b, _ := prvKey.GetPublic().Bytes() 67 | utils.LogInfof("peer pub key: %v", utils.GetHexStringFromBytes(b)) 68 | 69 | return prvKey, err 70 | } 71 | 72 | // NewNode create a new node with its implemented protocols 73 | func NewOperator(ctx context.Context, ds datastore.Batching, relay bool, bucketSize int) (*Operator, error) { 74 | cmgr := connmgr.NewConnManager(1500, 2000, time.Minute) 75 | 76 | // Ignoring most errors for brevity 77 | // See echo example for more details and better implementation 78 | 79 | prvKey, err := loadPrivateKey(ds) 80 | if err != nil { 81 | panic(err) 82 | } 83 | // listen, _ := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port)) 84 | 85 | opts := []libp2p.Option{libp2p.ConnectionManager(cmgr), libp2p.Identity(prvKey)} 86 | if relay { 87 | opts = append(opts, libp2p.EnableRelay(circuit.OptHop)) 88 | } 89 | 90 | host, err := libp2p.New(context.Background(), opts...) 91 | if err != nil { 92 | panic(err) 93 | } 94 | 95 | d2, err := dht.New(context.Background(), host, dhtopts.BucketSize(bucketSize), dhtopts.Datastore(ds), dhtopts.Validator(record.NamespacedValidator{ 96 | "pk": record.PublicKeyValidator{}, 97 | "ipns": ipns.Validator{KeyBook: host.Peerstore()}, 98 | })) 99 | _ = d2 100 | 101 | // Use an empty validator here for simplicity 102 | // CAUTION! Use d2 will cause a "stream reset" error! 103 | 104 | d1, err := dht.New(context.Background(), host, dhtopts.BucketSize(bucketSize), dhtopts.Datastore(ds), dhtopts.Validator(store.NodeValidator{})) 105 | if err != nil { 106 | panic(err) 107 | } 108 | 109 | // Pubsub 110 | ps, err := pubsub.NewGossipSub(ctx, host) 111 | if err != nil { 112 | panic(err) 113 | } 114 | 115 | op := &Operator{ 116 | dht: d1, 117 | pubsub: ps, 118 | db: ds, 119 | mdnsPeers: make(map[peer.ID]peer.AddrInfo), 120 | streams: make(chan network.Stream, 128), 121 | } 122 | s, _ := store.NewStore(d1, ds) 123 | cache, _ := store.NewCache() 124 | op.Context = localContext.NewContext(new(sync.RWMutex), host, s, cache, &ctx) 125 | 126 | vkData, err := ioutil.ReadFile("./snark/verification_key.json") 127 | if err != nil { 128 | panic(err) 129 | } 130 | op.Manager, _ = manager.NewManager(ps, d1, op.Context, string(vkData)) 131 | 132 | mdns, err := msdnDiscovery.NewMdnsService(ctx, host, time.Second*5, "") 133 | if err != nil { 134 | panic(err) 135 | } 136 | mdns.RegisterNotifee(op) 137 | 138 | return op, nil 139 | } 140 | 141 | // HandlePeerFound msdn handler 142 | func (o *Operator) HandlePeerFound(pi peer.AddrInfo) { 143 | o.Mutex.Lock() 144 | o.mdnsPeers[pi.ID] = pi 145 | o.Mutex.Unlock() 146 | 147 | if err := o.Context.Host.Connect(*o.Ctx, pi); err != nil { 148 | fmt.Printf("failed to connect to mDNS peer: %s\n", err) 149 | } 150 | } 151 | 152 | // Info ... 153 | func (o *Operator) Info() error { 154 | // 0b. Let's get a sense of what those defaults are. What transports are we 155 | // listening on? Each transport will have a multiaddr. If you run this 156 | // multiple times, you will get different port numbers. Note how we listen 157 | // on all interfaces by default. 158 | // fmt.Println("My addresses:") 159 | // for _, a := range o.h.Addrs() { 160 | // fmt.Printf("\t%s\n", a) 161 | // } 162 | 163 | fmt.Println() 164 | fmt.Println("My peer ID:") 165 | fmt.Printf("\t%s\n", o.Host.ID()) 166 | 167 | fmt.Println() 168 | fmt.Println("My identified multiaddrs:") 169 | for _, a := range o.Host.Addrs() { 170 | fmt.Printf("\t%s/p2p/%s\n", a, o.Host.ID()) 171 | } 172 | 173 | // What protocols are added by default? 174 | // fmt.Println() 175 | // fmt.Println("Protocols:") 176 | // for _, p := range o.h.Mux().Protocols() { 177 | // fmt.Printf("\t%s\n", p) 178 | // } 179 | 180 | // What peers do we have in our peerstore? (hint: we've connected to nobody so far). 181 | fmt.Println() 182 | fmt.Println("Peers in peerstore:") 183 | for _, p := range o.Host.Peerstore().PeersWithAddrs() { 184 | fmt.Printf("\t%s\n", p) 185 | } 186 | fmt.Println(len(o.Host.Peerstore().PeersWithAddrs())) 187 | 188 | // DHT routing table 189 | fmt.Println("DHT Routing table:") 190 | o.dht.RoutingTable().Print() 191 | 192 | // Connections 193 | fmt.Println("Connections:") 194 | fmt.Println(len(o.Host.Network().Conns())) 195 | 196 | fmt.Println("Created subjectHashHex:") 197 | for sh := range o.Cache.GetCreatedSubjects() { 198 | fmt.Println(sh) 199 | } 200 | 201 | fmt.Println("Collected subjects:") 202 | fmt.Println(o.Cache.GetCollectedSubjects()) 203 | 204 | fmt.Println("Subcribed topics:") 205 | fmt.Println(o.pubsub.GetTopics()) 206 | 207 | fmt.Println("Identity Index:") 208 | fmt.Println(o.GetIdentityIndex()) 209 | 210 | fmt.Println("Voter Identity Merkle Tree Contents:") 211 | fmt.Println(o.GetVoterIdentities()) 212 | 213 | fmt.Println("Ballot Map:") 214 | fmt.Println(o.GetBallotMaps()) 215 | 216 | return nil 217 | } 218 | 219 | // DHTBootstrap ... 220 | func (o *Operator) DHTBootstrap(seeds ...ma.Multiaddr) error { 221 | fmt.Println("Will bootstrap for 30 seconds...") 222 | 223 | ctx, cancel := context.WithTimeout(*o.Ctx, 30*time.Second) 224 | defer cancel() 225 | 226 | var wg sync.WaitGroup 227 | wg.Add(len(seeds)) 228 | 229 | for _, ma := range seeds { 230 | ai, err := peer.AddrInfoFromP2pAddr(ma) 231 | if err != nil { 232 | return err 233 | } 234 | 235 | go func(ai peer.AddrInfo) { 236 | defer wg.Done() 237 | 238 | fmt.Printf("Connecting to peer: %s\n", ai) 239 | if err := o.Host.Connect(ctx, ai); err != nil { 240 | fmt.Printf("Failed while connecting to peer: %s; %s\n", ai, err) 241 | } else { 242 | fmt.Printf("Succeeded while connecting to peer: %s\n", ai) 243 | } 244 | }(*ai) 245 | } 246 | 247 | wg.Wait() 248 | 249 | // if err := o.dht.BootstrapRandom(ctx); err != nil && err != context.DeadlineExceeded { 250 | // return fmt.Errorf("failed while bootstrapping DHT: %w", err) 251 | // } 252 | if err := o.dht.Bootstrap(ctx); err != nil && err != context.DeadlineExceeded { 253 | return fmt.Errorf("failed while bootstrapping DHT: %w", err) 254 | } 255 | 256 | fmt.Println("bootstrap OK! Routing table:") 257 | o.dht.RoutingTable().Print() 258 | 259 | return nil 260 | } 261 | 262 | // // Authenticate incoming p2p message 263 | // // message: a protobufs go data object 264 | // // data: common p2p message data 265 | // func (n *Node) authenticateMessage(message proto.Message, data *p2p.MessageData) bool { 266 | // // store a temp ref to signature and remove it from message data 267 | // // sign is a string to allow easy reset to zero-value (empty string) 268 | // sign := data.Sign 269 | // data.Sign = nil 270 | 271 | // // marshall data without the signature to protobufs3 binary format 272 | // bin, err := proto.Marshal(message) 273 | // if err != nil { 274 | // log.Println(err, "failed to marshal pb message") 275 | // return false 276 | // } 277 | 278 | // // restore sig in message data (for possible future use) 279 | // data.Sign = sign 280 | 281 | // // restore peer id binary format from base58 encoded node id data 282 | // peerId, err := peer.IDB58Decode(data.NodeId) 283 | // if err != nil { 284 | // log.Println(err, "Failed to decode node id from base58") 285 | // return false 286 | // } 287 | 288 | // // verify the data was authored by the signing peer identified by the public key 289 | // // and signature included in the message 290 | // return n.verifyData(bin, []byte(sign), peerId, data.NodePubKey) 291 | // } 292 | 293 | // // sign an outgoing p2p message payload 294 | // func (n *Node) signProtoMessage(message proto.Message) ([]byte, error) { 295 | // data, err := proto.Marshal(message) 296 | // if err != nil { 297 | // return nil, err 298 | // } 299 | // return n.signData(data) 300 | // } 301 | 302 | // // sign binary data using the local node's private key 303 | // func (n *Node) signData(data []byte) ([]byte, error) { 304 | // key := n.Peerstore().PrivKey(n.ID()) 305 | // res, err := key.Sign(data) 306 | // return res, err 307 | // } 308 | 309 | // Verify incoming p2p message data integrity 310 | // data: data to verify 311 | // signature: author signature provided in the message payload 312 | // peerId: author peer id from the message payload 313 | // pubKeyData: author public key from the message payload 314 | // func (n *Node) verifyData(data []byte, signature []byte, peerId peer.ID, pubKeyData []byte) bool { 315 | // key, err := crypto.UnmarshalPublicKey(pubKeyData) 316 | // if err != nil { 317 | // log.Println(err, "Failed to extract key from message key data") 318 | // return false 319 | // } 320 | 321 | // // extract node id from the provided public key 322 | // idFromKey, err := peer.IDFromPublicKey(key) 323 | 324 | // if err != nil { 325 | // log.Println(err, "Failed to extract peer id from public key") 326 | // return false 327 | // } 328 | 329 | // // verify that message author node id matches the provided node public key 330 | // if idFromKey != peerId { 331 | // log.Println(err, "Node id and provided public key mismatch") 332 | // return false 333 | // } 334 | 335 | // res, err := key.Verify(data, signature) 336 | // if err != nil { 337 | // log.Println(err, "Error authenticating data") 338 | // return false 339 | // } 340 | 341 | // return res 342 | // } 343 | 344 | // Run interactive commands 345 | func (o *Operator) Run() { 346 | commands := []struct { 347 | name string 348 | exec func() error 349 | }{ 350 | {"My info", o.handleMyInfo}, 351 | {"Manager: Propose a subject", o.handlePropose}, 352 | {"Manager: Join a subject", o.handleJoin}, 353 | {"Manager: Find topic providers", o.handleFindProposers}, 354 | {"Manager: Collect all topics", o.handleCollect}, 355 | // {"Manager: Sync identity index", o.handleSyncIdentityIndex}, 356 | {"Store: Put DHT", o.handlePutDHT}, 357 | {"Store: Get DHT", o.handleGetDHT}, 358 | {"Store: Put Local", o.handlePutLocal}, 359 | {"Store: Get Local", o.handleGetLocal}, 360 | {"DHT: Bootstrap (all seeds)", o.handleDHTBootstrap}, 361 | } 362 | 363 | var str []string 364 | for _, c := range commands { 365 | str = append(str, c.name) 366 | } 367 | 368 | for { 369 | sel := promptui.Select{ 370 | Label: "What do you want to do?", 371 | Items: str, 372 | Size: 1000, 373 | } 374 | 375 | fmt.Println() 376 | i, _, err := sel.Run() 377 | if err != nil { 378 | panic(err) 379 | } 380 | 381 | if err := commands[i].exec(); err != nil { 382 | fmt.Printf("command failed: %s\n", err) 383 | } 384 | } 385 | } 386 | 387 | func (o *Operator) handleMyInfo() error { 388 | return o.Info() 389 | } 390 | 391 | func (o *Operator) handleDHTBootstrap() error { 392 | return o.DHTBootstrap(dht.DefaultBootstrapPeers...) 393 | } 394 | 395 | func (o *Operator) handlePutDHT() error { 396 | // return o.Store.PutDHT() 397 | return nil 398 | } 399 | 400 | func (o *Operator) handlePutLocal() error { 401 | // return o.Store.PutLocal() 402 | return nil 403 | } 404 | 405 | func (o *Operator) handleGetDHT() error { 406 | // return o.Store.GetDHT() 407 | return nil 408 | } 409 | 410 | func (o *Operator) handleGetLocal() error { 411 | // return o.Store.GetLocal() 412 | return nil 413 | } 414 | 415 | func (o *Operator) handlePropose() error { 416 | p := promptui.Prompt{ 417 | Label: "Subject title", 418 | } 419 | title, err := p.Run() 420 | if err != nil { 421 | return err 422 | } 423 | 424 | p = promptui.Prompt{ 425 | Label: "Subject description", 426 | } 427 | description, err := p.Run() 428 | if err != nil { 429 | return err 430 | } 431 | 432 | return o.Propose(title, description, "") 433 | } 434 | 435 | func (o *Operator) handleJoin() error { 436 | p := promptui.Prompt{ 437 | Label: "Subject hash hex", 438 | } 439 | subjectHashHex, err := p.Run() 440 | if err != nil { 441 | return err 442 | } 443 | 444 | p = promptui.Prompt{ 445 | Label: "Identity commitment hex", 446 | } 447 | identityCommitmentHex, err := p.Run() 448 | if err != nil { 449 | return err 450 | } 451 | 452 | return o.Join(subjectHashHex, identityCommitmentHex) 453 | } 454 | 455 | // func (o *Operator) handleSyncIdentityIndex() error { 456 | // return o.SyncIdentityIndex() 457 | // } 458 | 459 | // func (o *Operator) handlePrintInboundMessages() error { 460 | // return o.PrintInboundMessages() 461 | // } 462 | 463 | func (o *Operator) handleFindProposers() error { 464 | peers, err := o.FindProposers() 465 | if err != nil { 466 | fmt.Println(err) 467 | } 468 | 469 | for p := range peers { 470 | fmt.Println("found peer", p) 471 | o.SetProvider(p.ID, "") 472 | } 473 | 474 | return nil 475 | } 476 | 477 | func (o *Operator) handleCollect() error { 478 | // subjects, err := o.Collect() 479 | // if err != nil { 480 | // fmt.Println(err) 481 | // } 482 | 483 | // for subject := range subjects { 484 | // fmt.Println("Collect: ", subject) 485 | // } 486 | 487 | return nil 488 | } 489 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/manager.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "github.com/libp2p/go-libp2p-core/discovery" 10 | "github.com/libp2p/go-libp2p-core/peer" 11 | routingDiscovery "github.com/libp2p/go-libp2p-discovery" 12 | dht "github.com/libp2p/go-libp2p-kad-dht" 13 | pubsub "github.com/libp2p/go-libp2p-pubsub" 14 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 15 | ba "github.com/unitychain/zkvote-node/zkvote/model/ballot" 16 | localContext "github.com/unitychain/zkvote-node/zkvote/model/context" 17 | id "github.com/unitychain/zkvote-node/zkvote/model/identity" 18 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 19 | pro "github.com/unitychain/zkvote-node/zkvote/operator/service/manager/protocol" 20 | "github.com/unitychain/zkvote-node/zkvote/operator/service/manager/voter" 21 | ) 22 | 23 | const KEY_SUBJECTS = "subjects" 24 | 25 | // Manager ... 26 | type Manager struct { 27 | *localContext.Context 28 | subjProtocol pro.Protocol 29 | idProtocol pro.Protocol 30 | ballotProtocol pro.Protocol 31 | 32 | ps *pubsub.PubSub 33 | dht *dht.IpfsDHT 34 | discovery discovery.Discovery 35 | providers map[peer.ID]string 36 | subjectProtocolCh chan []*subject.Subject 37 | voters map[subject.HashHex]*voter.Voter 38 | chAnnounce chan bool 39 | 40 | zkVerificationKey string 41 | 42 | idLock sync.Mutex 43 | ballotLock sync.Mutex 44 | } 45 | 46 | // NewManager ... 47 | func NewManager( 48 | pubsub *pubsub.PubSub, 49 | dht *dht.IpfsDHT, 50 | lc *localContext.Context, 51 | zkVerificationKey string, 52 | ) (*Manager, error) { 53 | // Discovery 54 | rd := routingDiscovery.NewRoutingDiscovery(dht) 55 | 56 | m := &Manager{ 57 | ps: pubsub, 58 | dht: dht, 59 | discovery: rd, 60 | Context: lc, 61 | providers: make(map[peer.ID]string), 62 | subjectProtocolCh: make(chan []*subject.Subject, 10), 63 | voters: make(map[subject.HashHex]*voter.Voter), 64 | chAnnounce: make(chan bool), 65 | zkVerificationKey: zkVerificationKey, 66 | idLock: sync.Mutex{}, 67 | ballotLock: sync.Mutex{}, 68 | } 69 | m.subjProtocol = pro.NewProtocol(pro.SubjectProtocolType, lc) 70 | m.idProtocol = pro.NewProtocol(pro.IdentityProtocolType, lc) 71 | m.ballotProtocol = pro.NewProtocol(pro.BallotProtocolType, lc) 72 | 73 | go m.announce() 74 | m.loadDB() 75 | 76 | go m.syncSubjectWorker() 77 | 78 | return m, nil 79 | } 80 | 81 | func finally() { 82 | err := recover() 83 | if err != nil { 84 | utils.LogErrorf("PANIC: %v", err) 85 | } 86 | } 87 | 88 | // 89 | // vote/identity function 90 | // 91 | // Propose a new subject 92 | func (m *Manager) Propose(title string, description string, identityCommitmentHex string) error { 93 | defer finally() 94 | 95 | utils.LogInfof("Propose, title:%v, desc:%v, id:%v", title, description, identityCommitmentHex) 96 | if 0 == len(title) || 0 == len(identityCommitmentHex) { 97 | utils.LogErrorf("Invalid input") 98 | return fmt.Errorf("invalid input") 99 | } 100 | 101 | voter, err := m.propose(title, description, identityCommitmentHex) 102 | if err != nil { 103 | utils.LogErrorf("Propose error, %v", err) 104 | return err 105 | } 106 | m.saveSubjects() 107 | m.saveSubjectContent(*voter.GetSubject().HashHex()) 108 | return nil 109 | } 110 | 111 | // Vote ... 112 | func (m *Manager) Vote(subjectHashHex string, proof string) error { 113 | defer finally() 114 | return m.silentVote(subjectHashHex, proof, false) 115 | } 116 | 117 | // Open ... 118 | func (m *Manager) Open(subjectHashHex string) (int, int) { 119 | defer finally() 120 | 121 | utils.LogInfof("Open subject: %v", subjectHashHex) 122 | voter, ok := m.voters[subject.HashHex(utils.Remove0x(subjectHashHex))] 123 | if !ok { 124 | utils.LogErrorf("Can't get voter with subject hash: %v", subject.HashHex(utils.Remove0x(subjectHashHex))) 125 | return -1, -1 126 | } 127 | return voter.Open() 128 | } 129 | 130 | // InsertIdentity ... 131 | func (m *Manager) InsertIdentity(subjectHashHex string, identityCommitmentHex string) error { 132 | defer finally() 133 | return m.insertIdentity(subjectHashHex, identityCommitmentHex, true) 134 | } 135 | 136 | // OverwriteIdentities ... 137 | func (m *Manager) OverwriteIdentities(subjectHashHex string, identitySet []string) error { 138 | defer finally() 139 | 140 | utils.LogInfof("overwrite, subject:%s", subjectHashHex) 141 | if 0 == len(subjectHashHex) || 0 == len(identitySet) { 142 | utils.LogErrorf("Invalid input") 143 | return fmt.Errorf("invalid input") 144 | } 145 | subjHex := subject.HashHex(utils.Remove0x(subjectHashHex)) 146 | voter, ok := m.voters[subjHex] 147 | if !ok { 148 | utils.LogErrorf("can't get voter with subject hash:%v", subjHex) 149 | return fmt.Errorf("can't get voter with subject hash:%v", subjHex) 150 | } 151 | 152 | // Convert to Identity 153 | set := make([]*id.Identity, 0) 154 | for _, idStr := range identitySet { 155 | set = append(set, id.NewIdentity(idStr)) 156 | } 157 | 158 | _, err := voter.OverwriteIds(set) 159 | if nil != err { 160 | utils.LogErrorf("identity pool registration error, %v", err.Error()) 161 | return err 162 | } 163 | 164 | m.saveSubjectContent(subjHex) 165 | return nil 166 | } 167 | 168 | // Join an existing subject 169 | func (m *Manager) Join(subjectHashHex string, identityCommitmentHex string) error { 170 | defer finally() 171 | 172 | utils.LogInfof("Join, subject:%s, id:%s", subjectHashHex, identityCommitmentHex) 173 | if 0 == len(subjectHashHex) || 0 == len(identityCommitmentHex) { 174 | utils.LogErrorf("Invalid input") 175 | return fmt.Errorf("invalid input") 176 | } 177 | 178 | subjHex := subject.HashHex(utils.Remove0x(subjectHashHex)) 179 | 180 | // No need to new a voter if the subjec is created by itself 181 | createdSubs := m.Cache.GetCreatedSubjects() 182 | if _, ok := createdSubs[subjHex]; ok { 183 | return m.insertIdentity(subjectHashHex, identityCommitmentHex, true) 184 | } 185 | 186 | collectedSubs := m.Cache.GetCollectedSubjects() 187 | if sub, ok := collectedSubs[subjHex]; ok { 188 | _, err := m.initAVoter(sub, identityCommitmentHex, false) 189 | if nil != err { 190 | utils.LogErrorf("Join, init voter error: %v", err) 191 | return err 192 | } 193 | 194 | // Sync identities 195 | ch, _ := m.SyncIdentities(subjHex) 196 | 197 | // Sync ballots 198 | go func(ch chan bool) { 199 | <-ch 200 | 201 | finished, err := m.SyncBallots(subjHex) 202 | if err != nil { 203 | utils.LogErrorf("SyncBallotIndex error, %v", err) 204 | } 205 | 206 | <-finished 207 | m.saveSubjects() 208 | m.saveSubjectContent(subjHex) 209 | }(ch) 210 | 211 | // TODO: return sync error 212 | return err 213 | } 214 | 215 | return fmt.Errorf("Can NOT find subject, %s", subjectHashHex) 216 | } 217 | 218 | // FindProposers ... 219 | func (m *Manager) FindProposers() (<-chan peer.AddrInfo, error) { 220 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 221 | _ = cancel 222 | 223 | peers, err := m.discovery.FindPeers(ctx, "subjects") 224 | if err != nil { 225 | return nil, err 226 | } 227 | 228 | return peers, err 229 | } 230 | 231 | // SetProvider ... 232 | func (m *Manager) SetProvider(key peer.ID, value string) { 233 | m.providers[key] = value 234 | } 235 | 236 | // GetProviders ... 237 | func (m *Manager) GetProviders() map[peer.ID]string { 238 | return m.providers 239 | } 240 | 241 | // GetProvider ... 242 | func (m *Manager) GetProvider(key peer.ID) string { 243 | return m.providers[key] 244 | } 245 | 246 | // 247 | // identity/subject getters 248 | // 249 | // GetSubjectList ... 250 | func (m *Manager) GetSubjectList() ([]*subject.Subject, error) { 251 | defer finally() 252 | 253 | result := make([]*subject.Subject, 0) 254 | // m.Collect() 255 | for _, s := range m.Cache.GetCollectedSubjects() { 256 | result = append(result, s) 257 | } 258 | for _, s := range m.Cache.GetCreatedSubjects() { 259 | result = append(result, s) 260 | } 261 | 262 | return result, nil 263 | } 264 | 265 | // // GetJoinedSubjectTitles ... 266 | // func (m *Manager) GetJoinedSubjectTitles() []string { 267 | // topics := m.ps.GetTopics() 268 | // fmt.Println(topics) 269 | 270 | // return topics 271 | // } 272 | 273 | // GetIdentityIndex ... 274 | func (m *Manager) GetIdentityIndex() map[subject.HashHex][]id.Identity { 275 | defer finally() 276 | 277 | index := make(map[subject.HashHex][]id.Identity) 278 | for k, v := range m.voters { 279 | index[k] = v.GetAllIdentities() 280 | } 281 | return index 282 | } 283 | 284 | // GetIdentityPath . 285 | // return intermediate values and merkle root in hex string 286 | func (m *Manager) GetIdentityPath( 287 | subjectHashHex string, 288 | identityCommitmentHex string) ( 289 | []string, []int, string, error) { 290 | defer finally() 291 | 292 | voter, ok := m.voters[subject.HashHex(utils.Remove0x(subjectHashHex))] 293 | if !ok { 294 | utils.LogWarningf("can't get voter with subject hash:%v", subject.HashHex(utils.Remove0x(subjectHashHex))) 295 | return nil, nil, "", fmt.Errorf("can't get voter with subject hash:%v", subject.HashHex(utils.Remove0x(subjectHashHex))) 296 | } 297 | idPaths, idPathIndexes, root, err := voter.GetIdentityPath(*id.NewIdentity(identityCommitmentHex)) 298 | if err != nil { 299 | return nil, nil, "", err 300 | } 301 | hexIDPaths := make([]string, len(idPaths)) 302 | for i, p := range idPaths { 303 | hexIDPaths[i] = p.Hex() 304 | } 305 | return hexIDPaths, idPathIndexes, root.Hex(), nil 306 | } 307 | 308 | // 309 | // CLI Debugger 310 | // 311 | // GetVoterIdentities ... 312 | func (m *Manager) GetVoterIdentities() map[subject.HashHex][]*id.IdPathElement { 313 | result := make(map[subject.HashHex][]*id.IdPathElement) 314 | 315 | for k, v := range m.voters { 316 | result[k] = v.GetAllIds() 317 | } 318 | return result 319 | } 320 | 321 | // GetBallotMaps ... 322 | func (m *Manager) GetBallotMaps() map[subject.HashHex]ba.Map { 323 | result := make(map[subject.HashHex]ba.Map) 324 | 325 | for k, v := range m.voters { 326 | result[k] = v.GetBallotMap() 327 | } 328 | return result 329 | } 330 | 331 | // 332 | // internal functions 333 | // 334 | 335 | func (m *Manager) propose(title string, description string, identityCommitmentHex string) (*voter.Voter, error) { 336 | // Store the new subject locally 337 | identity := id.NewIdentity(identityCommitmentHex) 338 | if nil == identity { 339 | return nil, fmt.Errorf("Can not get identity object by commitment %v", identityCommitmentHex) 340 | } 341 | subject := subject.NewSubject(title, description, identity) 342 | if _, ok := m.voters[*subject.HashHex()]; ok { 343 | return nil, fmt.Errorf("subject already existed") 344 | } 345 | 346 | voter, err := m.initAVoter(subject, identityCommitmentHex, true) 347 | if nil != err { 348 | return nil, err 349 | } 350 | 351 | // Store the created subject 352 | m.Cache.InsertCreatedSubject(*subject.HashHex(), subject) 353 | 354 | return voter, nil 355 | } 356 | 357 | func (m *Manager) silentVote(subjectHashHex string, proof string, silent bool) error { 358 | utils.LogInfof("Vote, subject:%s", subjectHashHex) 359 | if 0 == len(subjectHashHex) || 0 == len(proof) { 360 | utils.LogErrorf("Invalid input") 361 | return fmt.Errorf("invalid input") 362 | } 363 | 364 | subjHex := subject.HashHex(utils.Remove0x(subjectHashHex)) 365 | voter, ok := m.voters[subjHex] 366 | if !ok { 367 | utils.LogErrorf("Can't get voter with subject hash: %v", subjHex) 368 | return fmt.Errorf("Can't get voter with subject hash: %v", subjHex) 369 | } 370 | ballot, err := ba.NewBallot(proof) 371 | if err != nil { 372 | return err 373 | } 374 | 375 | err = voter.Vote(ballot, silent) 376 | if err != nil { 377 | return err 378 | } 379 | 380 | m.saveSubjectContent(subjHex) 381 | return nil 382 | } 383 | 384 | func (m *Manager) insertIdentity(subjectHashHex string, identityCommitmentHex string, publish bool) error { 385 | utils.LogInfof("Insert, subject:%s, id:%v", subjectHashHex, identityCommitmentHex) 386 | if 0 == len(subjectHashHex) || 0 == len(identityCommitmentHex) { 387 | utils.LogWarningf("Invalid input") 388 | return fmt.Errorf("invalid input") 389 | } 390 | 391 | voter, ok := m.voters[subject.HashHex(utils.Remove0x(subjectHashHex))] 392 | if !ok { 393 | return fmt.Errorf("Can't get voter with subject hash: %v", subject.HashHex(utils.Remove0x(subjectHashHex))) 394 | } 395 | 396 | _, err := voter.InsertIdentity(id.NewIdentity(identityCommitmentHex), publish) 397 | if nil != err { 398 | utils.LogWarningf("identity pool registration error, %v", err.Error()) 399 | return err 400 | } 401 | 402 | m.saveSubjectContent(subject.HashHex(subjectHashHex)) 403 | return nil 404 | } 405 | 406 | func (m *Manager) initAVoter(sub *subject.Subject, idc string, publish bool) (*voter.Voter, error) { 407 | // New a voter including proposal/id tree 408 | utils.LogDebug("New a voter") 409 | voter, err := voter.NewVoter(sub, m.ps, m.Context, m.zkVerificationKey) 410 | if nil != err { 411 | return nil, err 412 | } 413 | 414 | utils.LogInfof("Register, subject:%s, id:%v", sub.HashHex().String(), idc) 415 | identity := id.NewIdentity(idc) 416 | // Insert idenitty to identity pool 417 | _, err = voter.InsertIdentity(identity, publish) 418 | if nil != err { 419 | return nil, err 420 | } 421 | 422 | m.voters[*sub.HashHex()] = voter 423 | 424 | if nil != m.chAnnounce { 425 | m.chAnnounce <- true 426 | } 427 | 428 | return m.voters[*sub.HashHex()], nil 429 | } 430 | 431 | // Announce that the node has a proposal to be discovered 432 | func (m *Manager) announce() error { 433 | <-m.chAnnounce 434 | close(m.chAnnounce) 435 | m.chAnnounce = nil 436 | 437 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 438 | defer cancel() 439 | 440 | // TODO: Check if the voter is ready for announcement 441 | utils.LogInfo("Announce") 442 | _, err := m.discovery.Advertise(ctx, "subjects", routingDiscovery.TTL(10*time.Minute)) 443 | if err != nil { 444 | utils.LogWarningf("Advertise error, %v", err) 445 | } 446 | return err 447 | } 448 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/manager_db.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | ba "github.com/unitychain/zkvote-node/zkvote/model/ballot" 8 | id "github.com/unitychain/zkvote-node/zkvote/model/identity" 9 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 10 | 11 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 12 | ) 13 | 14 | type storeObject struct { 15 | Subject subject.Subject `json:"subject"` 16 | Ids []id.Identity `json:"ids"` 17 | BallotMap ba.Map `json:"ballots"` 18 | } 19 | 20 | func (m *Manager) save(key string, v interface{}) error { 21 | jsonStr, err := json.Marshal(v) 22 | if err != nil { 23 | utils.LogErrorf("Marshal error %v", err.Error()) 24 | return err 25 | } 26 | // utils.LogDebugf("save, key: %v, value:%v", key, string(jsonStr)) 27 | err = m.Store.PutLocal(key, string(jsonStr)) 28 | if err != nil { 29 | utils.LogErrorf("Put local db error, %v", err) 30 | return err 31 | } 32 | 33 | return nil 34 | } 35 | 36 | func (m *Manager) saveSubjects() error { 37 | subs := make([]subject.HashHex, len(m.voters)) 38 | i := 0 39 | for k := range m.voters { 40 | subs[i] = k 41 | i++ 42 | } 43 | 44 | return m.save(KEY_SUBJECTS, subs) 45 | } 46 | 47 | func (m *Manager) saveSubjectContent(subHex subject.HashHex) error { 48 | voter, ok := m.voters[subHex] 49 | if !ok { 50 | return fmt.Errorf("Can't get voter with subject hash: %v", subHex) 51 | } 52 | ids := voter.GetAllIdentities() 53 | ballotMap := voter.GetBallotMap() 54 | subj := voter.GetSubject() 55 | 56 | s := &storeObject{ 57 | Subject: *subj, 58 | Ids: ids, 59 | BallotMap: ballotMap, 60 | } 61 | return m.save(subHex.Hash().Hex().String(), s) 62 | } 63 | 64 | func (m *Manager) loadSubjects() ([]subject.HashHex, error) { 65 | value, err := m.Store.GetLocal(KEY_SUBJECTS) 66 | if err != nil { 67 | utils.LogErrorf("Get local db error, %v", err) 68 | return nil, err 69 | } 70 | 71 | var subs []subject.HashHex 72 | err = json.Unmarshal([]byte(value), &subs) 73 | if err != nil { 74 | utils.LogErrorf("unmarshal subjects error, %v", err) 75 | } 76 | 77 | utils.LogDebugf("loaded subjects hex: %v", subs) 78 | return subs, err 79 | } 80 | 81 | func (m *Manager) loadSubjectContent(subHex subject.HashHex) (*storeObject, error) { 82 | 83 | utils.LogDebugf("load subject: %s", subHex.String()) 84 | value, err := m.Store.GetLocal(subHex.Hash().Hex().String()) 85 | if err != nil { 86 | utils.LogErrorf("Get local db error, %v", err) 87 | return nil, err 88 | } 89 | 90 | var obj storeObject 91 | err = json.Unmarshal([]byte(value), &obj) 92 | if err != nil { 93 | utils.LogErrorf("unmarshal content of subject error, %v", err) 94 | return nil, err 95 | } 96 | return &obj, nil 97 | } 98 | 99 | func (m *Manager) loadDB() { 100 | subsHex, _ := m.loadSubjects() 101 | for _, s := range subsHex { 102 | if 0 == len(s.String()) { 103 | continue 104 | } 105 | 106 | obj, err := m.loadSubjectContent(s) 107 | if err != nil { 108 | continue 109 | } 110 | 111 | m.propose(obj.Subject.GetTitle(), obj.Subject.GetDescription(), obj.Subject.GetProposer().String()) 112 | 113 | for _, id := range obj.Ids { 114 | if id.Equal(obj.Subject.GetProposer()) { 115 | continue 116 | } 117 | m.insertIdentity(utils.Remove0x(obj.Subject.HashHex().String()), id.String(), true) 118 | } 119 | 120 | go func() { 121 | for _, b := range obj.BallotMap { 122 | jStr, err := b.JSON() 123 | if err != nil { 124 | utils.LogWarningf("get json ballot error, %v", err) 125 | break 126 | } 127 | m.silentVote(utils.Remove0x(obj.Subject.HashHex().String()), jStr, true) 128 | } 129 | }() 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/manager_pubsub.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 8 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 9 | ) 10 | 11 | func (m *Manager) syncSubjectWorker() { 12 | for { 13 | m.SyncSubjects() 14 | time.Sleep(60 * time.Second) 15 | } 16 | } 17 | 18 | func (m *Manager) waitSubject(ch chan []string) { 19 | select { 20 | case results := <-ch: 21 | for _, ret := range results { 22 | var s subject.Subject 23 | err := json.Unmarshal([]byte(ret), &s) 24 | if err != nil { 25 | utils.LogWarningf("Unmarshal error, %v", err) 26 | continue 27 | } 28 | m.Cache.InsertColletedSubject(*s.HashHex(), &s) 29 | } 30 | case <-time.After(30 * time.Second): 31 | utils.LogWarning("waitSubject timeout") 32 | } 33 | 34 | close(ch) 35 | } 36 | 37 | // SyncSubject ... 38 | func (m *Manager) SyncSubjects() { 39 | defer finally() 40 | 41 | proposers, err := m.FindProposers() 42 | if err != nil { 43 | utils.LogErrorf("find peers error, %v", err) 44 | return 45 | } 46 | 47 | // TODO: store peers 48 | for peer := range proposers { 49 | // Ignore self ID 50 | if peer.ID == m.Host.ID() { 51 | continue 52 | } 53 | utils.LogInfof("found peer, %v", peer) 54 | m.Host.Peerstore().AddAddrs(peer.ID, peer.Addrs, 24*time.Hour) 55 | 56 | ch := make(chan []string) 57 | m.subjProtocol.SubmitRequest(peer.ID, nil, ch) 58 | go m.waitSubject(ch) 59 | } 60 | } 61 | 62 | // 63 | // Synchronizing functions 64 | // 65 | 66 | func (m *Manager) waitIdentities(subjHex subject.HashHex, chIDStrSet chan []string, finished chan bool) { 67 | defer func() { 68 | err := recover() 69 | if err != nil { 70 | utils.LogWarningf("PANIC: %v", err) 71 | } 72 | close(chIDStrSet) 73 | m.idLock.Unlock() 74 | }() 75 | 76 | m.idLock.Lock() 77 | select { 78 | case idStrSet := <-chIDStrSet: 79 | // CAUTION! 80 | // Manager needs to overwrite the whole identity pool 81 | // to keep the order of the tree the same 82 | m.OverwriteIdentities(subjHex.String(), idStrSet) 83 | case <-time.After(30 * time.Second): 84 | utils.LogWarning("waitIdentities timeout") 85 | } 86 | 87 | finished <- true 88 | } 89 | 90 | // TODO: move to voter.go 91 | // SyncIdentity ... 92 | func (m *Manager) SyncIdentities(subjHex subject.HashHex) (chan bool, error) { 93 | defer finally() 94 | 95 | voter := m.voters[subjHex] 96 | subjHash := subjHex.Hash() 97 | 98 | // Get peers from the same pubsub 99 | strTopic := voter.GetIdentitySub().Topic() 100 | peers := m.ps.ListPeers(strTopic) 101 | utils.LogDebugf("SyncIdentities peers: %v", peers) 102 | 103 | chPeers := make(chan bool, len(peers)) 104 | for _, peer := range peers { 105 | ch := make(chan []string) 106 | m.idProtocol.SubmitRequest(peer, &subjHash, ch) 107 | go m.waitIdentities(subjHash.Hex(), ch, chPeers) 108 | } 109 | return chPeers, nil 110 | } 111 | 112 | func (m *Manager) waitBallots(subjHex subject.HashHex, chBallotStrSet chan []string, finished chan bool) { 113 | defer func() { 114 | err := recover() 115 | if err != nil { 116 | utils.LogWarningf("PANIC: %v", err) 117 | } 118 | close(chBallotStrSet) 119 | m.ballotLock.Unlock() 120 | }() 121 | 122 | m.ballotLock.Lock() 123 | 124 | select { 125 | case ballotStrSet := <-chBallotStrSet: 126 | utils.LogDebugf("ballot num: %d", len(ballotStrSet)) 127 | for _, bs := range ballotStrSet { 128 | err := m.silentVote(subjHex.String(), bs, true) 129 | if err != nil { 130 | utils.LogErrorf("waitBallots, vote error, %v", err.Error()) 131 | } 132 | } 133 | case <-time.After(30 * time.Second): 134 | utils.LogWarning("waitBallots timeout") 135 | } 136 | 137 | finished <- true 138 | } 139 | 140 | // SyncBallot ... 141 | func (m *Manager) SyncBallots(subjHex subject.HashHex) (chan bool, error) { 142 | defer finally() 143 | 144 | voter := m.voters[subjHex] 145 | subjHash := subjHex.Hash() 146 | // Get peers from the same pubsub 147 | peers := m.ps.ListPeers(voter.GetVoteSub().Topic()) 148 | utils.LogDebugf("SyncBallots peers: %v", peers) 149 | 150 | chPeers := make(chan bool, len(peers)) 151 | for _, peer := range peers { 152 | ch := make(chan []string) 153 | m.ballotProtocol.SubmitRequest(peer, &subjHash, ch) 154 | go m.waitBallots(subjHash.Hex(), ch, chPeers) 155 | } 156 | return chPeers, nil 157 | } 158 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/protocol/ballot_protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | 7 | proto "github.com/gogo/protobuf/proto" 8 | uuid "github.com/google/uuid" 9 | "github.com/libp2p/go-libp2p-core/network" 10 | "github.com/libp2p/go-libp2p-core/peer" 11 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 12 | "github.com/unitychain/zkvote-node/zkvote/model/context" 13 | pb "github.com/unitychain/zkvote-node/zkvote/model/pb" 14 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 15 | ) 16 | 17 | // pattern: /protocol-name/request-or-response-message/version 18 | const ballotRequest = "/ballot/req/0.0.1" 19 | const ballotResponse = "/ballot/res/0.0.1" 20 | 21 | // BallotProtocol type 22 | type BallotProtocol struct { 23 | channels map[peer.ID]map[subject.HashHex]chan<- []string 24 | context *context.Context 25 | requests map[string]*pb.BallotRequest // used to access request data from response handlers 26 | } 27 | 28 | // NewBallotProtocol ... 29 | func NewBallotProtocol(context *context.Context) Protocol { 30 | sp := &BallotProtocol{ 31 | context: context, 32 | requests: make(map[string]*pb.BallotRequest), 33 | } 34 | sp.channels = make(map[peer.ID]map[subject.HashHex]chan<- []string) 35 | sp.context.Host.SetStreamHandler(ballotRequest, sp.onRequest) 36 | sp.context.Host.SetStreamHandler(ballotResponse, sp.onResponse) 37 | return sp 38 | } 39 | 40 | // remote peer requests handler 41 | 42 | func (sp *BallotProtocol) onRequest(s network.Stream) { 43 | 44 | // get request data 45 | data := &pb.BallotRequest{} 46 | buf, err := ioutil.ReadAll(s) 47 | if err != nil { 48 | s.Reset() 49 | utils.LogErrorf("%v", err) 50 | return 51 | } 52 | s.Close() 53 | 54 | // unmarshal it 55 | proto.Unmarshal(buf, data) 56 | if err != nil { 57 | utils.LogErrorf("%v", err) 58 | return 59 | } 60 | 61 | utils.LogInfof("Received ballot request from %s. Message: %s", s.Conn().RemotePeer(), data.Message) 62 | 63 | // generate response message 64 | // utils.LogInfof("Sending ballot response to %s. Message id: %s...", s.Conn().RemotePeer(), data.Metadata.Id) 65 | 66 | // List ballot index 67 | subjectHash := subject.Hash(data.SubjectHash) 68 | var ballotSet []string 69 | set := sp.context.Cache.GetBallotSet(subjectHash.Hex()) 70 | // set, err := sp.manager.GetBallotSet(&subjectHash) 71 | for _, h := range set { 72 | s, _ := h.JSON() 73 | ballotSet = append(ballotSet, s) 74 | } 75 | resp := &pb.BallotResponse{Metadata: NewMetadata(sp.context.Host, data.Metadata.Id, false), 76 | Message: fmt.Sprintf("Ballot response from %s", sp.context.Host.ID()), SubjectHash: subjectHash.Byte(), BallotSet: ballotSet} 77 | 78 | // send the response 79 | ok := SendProtoMessage(sp.context.Host, s.Conn().RemotePeer(), ballotResponse, resp) 80 | if ok { 81 | utils.LogInfof("Ballot response(%v) to %s sent.", ballotSet, s.Conn().RemotePeer().String()) 82 | } 83 | } 84 | 85 | // remote ping response handler 86 | func (sp *BallotProtocol) onResponse(s network.Stream) { 87 | 88 | data := &pb.BallotResponse{} 89 | buf, err := ioutil.ReadAll(s) 90 | if err != nil { 91 | s.Reset() 92 | utils.LogErrorf("%v", err) 93 | return 94 | } 95 | s.Close() 96 | 97 | // unmarshal it 98 | proto.Unmarshal(buf, data) 99 | if err != nil { 100 | utils.LogErrorf("%v", err) 101 | return 102 | } 103 | 104 | defer func() { 105 | err := recover() 106 | if err != nil { 107 | utils.LogWarningf("panic: %v", err) 108 | } 109 | }() 110 | // utils.LogDebugf("response, ballot %v", data.BallotSet) 111 | subjectHash := subject.Hash(data.SubjectHash) 112 | ch := sp.channels[s.Conn().RemotePeer()][subjectHash.Hex()] 113 | ch <- data.BallotSet 114 | 115 | // locate request data and remove it if found 116 | _, ok := sp.requests[data.Metadata.Id] 117 | if ok { 118 | // remove request from map as we have processed it here 119 | delete(sp.requests, data.Metadata.Id) 120 | } else { 121 | utils.LogWarning("Failed to locate request data boject for response") 122 | return 123 | } 124 | 125 | utils.LogInfof("Received ballot response from %s. Message id:%s. Message: %s.", s.Conn().RemotePeer(), data.Metadata.Id, data.Message) 126 | } 127 | 128 | // SubmitRequest ... 129 | // TODO: use callback instead of channel 130 | func (sp *BallotProtocol) SubmitRequest(peerID peer.ID, subjectHash *subject.Hash, ch chan<- []string) bool { 131 | utils.LogInfof("Sending ballot request to: %s....", peerID) 132 | 133 | // create message data 134 | req := &pb.BallotRequest{Metadata: NewMetadata(sp.context.Host, uuid.New().String(), false), 135 | Message: fmt.Sprintf("Ballot request from %s", sp.context.Host.ID()), SubjectHash: subjectHash.Byte()} 136 | 137 | ok := SendProtoMessage(sp.context.Host, peerID, ballotRequest, req) 138 | if !ok { 139 | return false 140 | } 141 | 142 | // store ref request so response handler has access to it 143 | sp.requests[req.Metadata.Id] = req 144 | 145 | chPeer := sp.channels[peerID] 146 | if chPeer == nil { 147 | sp.channels[peerID] = make(map[subject.HashHex]chan<- []string) 148 | } 149 | sp.channels[peerID][subjectHash.Hex()] = ch 150 | // utils.LogInfof("Ballot request to: %s was sent. Message Id: %s, Message: %s", peerID, req.Metadata.Id, req.Message) 151 | return true 152 | } 153 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/protocol/factory.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "github.com/unitychain/zkvote-node/zkvote/model/context" 4 | 5 | type ProtocolType int 6 | 7 | const ( 8 | BallotProtocolType ProtocolType = 1 << iota 9 | IdentityProtocolType 10 | SubjectProtocolType 11 | ) 12 | 13 | // NewProtocol . 14 | func NewProtocol(t ProtocolType, context *context.Context) Protocol { 15 | switch t { 16 | case BallotProtocolType: 17 | return NewBallotProtocol(context) 18 | case IdentityProtocolType: 19 | return NewIdentityProtocol(context) 20 | case SubjectProtocolType: 21 | return NewSubjectProtocol(context) 22 | } 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/protocol/identity_protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | 7 | proto "github.com/gogo/protobuf/proto" 8 | uuid "github.com/google/uuid" 9 | "github.com/libp2p/go-libp2p-core/network" 10 | "github.com/libp2p/go-libp2p-core/peer" 11 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 12 | "github.com/unitychain/zkvote-node/zkvote/model/context" 13 | pb "github.com/unitychain/zkvote-node/zkvote/model/pb" 14 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 15 | ) 16 | 17 | // pattern: /protocol-name/request-or-response-message/version 18 | const identityRequest = "/identity/req/0.0.1" 19 | const identityResponse = "/identity/res/0.0.1" 20 | 21 | // IdentityProtocol type 22 | type IdentityProtocol struct { 23 | channels map[peer.ID]map[subject.HashHex]chan<- []string 24 | context *context.Context 25 | requests map[string]*pb.IdentityRequest // used to access request data from response handlers 26 | } 27 | 28 | // NewIdentityProtocol ... 29 | func NewIdentityProtocol(context *context.Context) Protocol { 30 | sp := &IdentityProtocol{ 31 | context: context, 32 | requests: make(map[string]*pb.IdentityRequest), 33 | } 34 | sp.channels = make(map[peer.ID]map[subject.HashHex]chan<- []string) 35 | sp.context.Host.SetStreamHandler(identityRequest, sp.onRequest) 36 | sp.context.Host.SetStreamHandler(identityResponse, sp.onResponse) 37 | return sp 38 | } 39 | 40 | // remote peer requests handler 41 | func (sp *IdentityProtocol) onRequest(s network.Stream) { 42 | 43 | // get request data 44 | data := &pb.IdentityRequest{} 45 | buf, err := ioutil.ReadAll(s) 46 | if err != nil { 47 | s.Reset() 48 | utils.LogErrorf("%v", err) 49 | return 50 | } 51 | s.Close() 52 | 53 | // unmarshal it 54 | proto.Unmarshal(buf, data) 55 | if err != nil { 56 | utils.LogErrorf("%v", err) 57 | return 58 | } 59 | 60 | utils.LogInfof("Received identity request from %s. Message: %s", s.Conn().RemotePeer(), data.Message) 61 | 62 | // generate response message 63 | // utils.LogInfof("Sending identity response to %s. Message id: %s...", s.Conn().RemotePeer(), data.Metadata.Id) 64 | 65 | // List identity index 66 | subjectHash := subject.Hash(data.SubjectHash) 67 | var identitySet []string 68 | set := sp.context.Cache.GetIdentitySet(subjectHash.Hex()) 69 | // set, err := sp.manager.GetIdentitySet(&subjectHash) 70 | for k := range set { 71 | identitySet = append(identitySet, k.String()) 72 | } 73 | resp := &pb.IdentityResponse{Metadata: NewMetadata(sp.context.Host, data.Metadata.Id, false), 74 | Message: fmt.Sprintf("Identity response from %s", sp.context.Host.ID()), SubjectHash: subjectHash.Byte(), IdentitySet: identitySet} 75 | 76 | // send the response 77 | ok := SendProtoMessage(sp.context.Host, s.Conn().RemotePeer(), identityResponse, resp) 78 | 79 | if ok { 80 | utils.LogInfof("Identity response(%v) to %s sent.", set, s.Conn().RemotePeer().String()) 81 | } 82 | } 83 | 84 | // remote ping response handler 85 | func (sp *IdentityProtocol) onResponse(s network.Stream) { 86 | 87 | data := &pb.IdentityResponse{} 88 | buf, err := ioutil.ReadAll(s) 89 | if err != nil { 90 | s.Reset() 91 | utils.LogErrorf("%v", err) 92 | return 93 | } 94 | s.Close() 95 | 96 | // unmarshal it 97 | proto.Unmarshal(buf, data) 98 | if err != nil { 99 | utils.LogErrorf("%v", err) 100 | return 101 | } 102 | 103 | defer func() { 104 | err := recover() 105 | if err != nil { 106 | utils.LogWarningf("panic: %v", err) 107 | } 108 | }() 109 | // Store all identityHash 110 | subjectHash := subject.Hash(data.SubjectHash) 111 | ch := sp.channels[s.Conn().RemotePeer()][subjectHash.Hex()] 112 | ch <- data.IdentitySet 113 | 114 | // locate request data and remove it if found 115 | _, ok := sp.requests[data.Metadata.Id] 116 | if ok { 117 | // remove request from map as we have processed it here 118 | delete(sp.requests, data.Metadata.Id) 119 | } else { 120 | utils.LogWarningf("Failed to locate request data boject for response") 121 | return 122 | } 123 | 124 | utils.LogInfof("Received identity response from %s. Message id:%s. Message: %s.", s.Conn().RemotePeer(), data.Metadata.Id, data.Message) 125 | } 126 | 127 | // SubmitRequest ... 128 | // TODO: use callback instead of channel 129 | func (sp *IdentityProtocol) SubmitRequest(peerID peer.ID, subjectHash *subject.Hash, ch chan<- []string) bool { 130 | utils.LogInfof("Sending identity request to: %s....", peerID) 131 | 132 | // create message data 133 | req := &pb.IdentityRequest{Metadata: NewMetadata(sp.context.Host, uuid.New().String(), false), 134 | Message: fmt.Sprintf("Identity request from %s", sp.context.Host.ID()), SubjectHash: subjectHash.Byte()} 135 | 136 | ok := SendProtoMessage(sp.context.Host, peerID, identityRequest, req) 137 | if !ok { 138 | return false 139 | } 140 | 141 | chPeer := sp.channels[peerID] 142 | if chPeer == nil { 143 | sp.channels[peerID] = make(map[subject.HashHex]chan<- []string) 144 | } 145 | sp.channels[peerID][subjectHash.Hex()] = ch 146 | // store ref request so response handler has access to it 147 | sp.requests[req.Metadata.Id] = req 148 | // utils.LogInfof("Identity request to: %s was sent. Message Id: %s, Message: %s", peerID, req.Metadata.Id, req.Message) 149 | return true 150 | } 151 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/protocol/network.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | ggio "github.com/gogo/protobuf/io" 9 | proto "github.com/gogo/protobuf/proto" 10 | "github.com/libp2p/go-libp2p-core/helpers" 11 | "github.com/libp2p/go-libp2p-core/host" 12 | "github.com/libp2p/go-libp2p-core/peer" 13 | "github.com/libp2p/go-libp2p-core/protocol" 14 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 15 | pb "github.com/unitychain/zkvote-node/zkvote/model/pb" 16 | ) 17 | 18 | // SendProtoMessage helper method - writes a protobuf go data object to a network stream 19 | // data: reference of protobuf go data object to send (not the object itself) 20 | // s: network stream to write the data to 21 | func SendProtoMessage(host host.Host, id peer.ID, p protocol.ID, data proto.Message) bool { 22 | s, err := host.NewStream(context.Background(), id, p) 23 | if err != nil { 24 | log.Println(err) 25 | return false 26 | } 27 | writer := ggio.NewFullWriter(s) 28 | err = writer.WriteMsg(data) 29 | if err != nil { 30 | log.Println(err) 31 | s.Reset() 32 | return false 33 | } 34 | // FullClose closes the stream and waits for the other side to close their half. 35 | err = helpers.FullClose(s) 36 | if err != nil { 37 | log.Println(err) 38 | s.Reset() 39 | return false 40 | } 41 | return true 42 | } 43 | 44 | // NewMetadata helper method - generate message data shared between all node's p2p protocols 45 | // messageId: unique for requests, copied from request for responses 46 | func NewMetadata(host host.Host, messageID string, gossip bool) *pb.Metadata { 47 | // Add protobufs bin data for message author public key 48 | // this is useful for authenticating messages forwarded by a node authored by another node 49 | nodePubKey, err := host.Peerstore().PubKey(host.ID()).Bytes() 50 | 51 | if err != nil { 52 | panic("Failed to get public key for sender from local peer store.") 53 | } 54 | 55 | return &pb.Metadata{ 56 | ClientVersion: utils.ClientVersion, 57 | NodeId: peer.IDB58Encode(host.ID()), 58 | NodePubKey: nodePubKey, 59 | Timestamp: time.Now().Unix(), 60 | Id: messageID, 61 | Gossip: gossip} 62 | } 63 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "github.com/libp2p/go-libp2p-core/network" 5 | "github.com/libp2p/go-libp2p-core/peer" 6 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 7 | ) 8 | 9 | type Protocol interface { 10 | onRequest(s network.Stream) 11 | onResponse(s network.Stream) 12 | 13 | SubmitRequest(peerID peer.ID, subjectHash *subject.Hash, ch chan<- []string) bool 14 | } 15 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/protocol/subject_protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | 8 | "github.com/libp2p/go-libp2p-core/network" 9 | "github.com/libp2p/go-libp2p-core/peer" 10 | 11 | proto "github.com/gogo/protobuf/proto" 12 | uuid "github.com/google/uuid" 13 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 14 | "github.com/unitychain/zkvote-node/zkvote/model/context" 15 | "github.com/unitychain/zkvote-node/zkvote/model/identity" 16 | pb "github.com/unitychain/zkvote-node/zkvote/model/pb" 17 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 18 | ) 19 | 20 | // pattern: /protocol-name/request-or-response-message/version 21 | const subjectRequest = "/subject/req/0.0.1" 22 | const subjectResponse = "/subject/res/0.0.1" 23 | 24 | // SubjectProtocol type 25 | type SubjectProtocol struct { 26 | channel map[peer.ID]chan<- []string 27 | context *context.Context 28 | requests map[string]*pb.SubjectRequest // used to access request data from response handlers 29 | } 30 | 31 | // NewSubjectProtocol ... 32 | func NewSubjectProtocol(context *context.Context) Protocol { 33 | sp := &SubjectProtocol{ 34 | context: context, 35 | requests: make(map[string]*pb.SubjectRequest), 36 | } 37 | sp.channel = make(map[peer.ID]chan<- []string) 38 | sp.context.Host.SetStreamHandler(subjectRequest, sp.onRequest) 39 | sp.context.Host.SetStreamHandler(subjectResponse, sp.onResponse) 40 | return sp 41 | } 42 | 43 | // remote peer requests handler 44 | func (sp *SubjectProtocol) onRequest(s network.Stream) { 45 | 46 | // get request data 47 | data := &pb.SubjectRequest{} 48 | buf, err := ioutil.ReadAll(s) 49 | if err != nil { 50 | s.Reset() 51 | utils.LogErrorf("%v", err) 52 | return 53 | } 54 | s.Close() 55 | 56 | // unmarshal it 57 | proto.Unmarshal(buf, data) 58 | if err != nil { 59 | utils.LogErrorf("%v", err) 60 | return 61 | } 62 | 63 | utils.LogInfof("Received subject request from %s. Message: %s", s.Conn().RemotePeer(), data.Message) 64 | 65 | // generate response message 66 | // utils.LogInfof("Sending subject response to %s. Message id: %s...", s.Conn().RemotePeer(), data.Metadata.Id) 67 | 68 | // List created subjects 69 | subjects := make([]*pb.Subject, 0) 70 | for _, s := range sp.context.Cache.GetCreatedSubjects() { 71 | identity := s.GetProposer() 72 | subject := &pb.Subject{Title: s.GetTitle(), Description: s.GetDescription(), Proposer: identity.String()} 73 | subjects = append(subjects, subject) 74 | } 75 | for _, s := range sp.context.Cache.GetCollectedSubjects() { 76 | identity := s.GetProposer() 77 | subject := &pb.Subject{Title: s.GetTitle(), Description: s.GetDescription(), Proposer: identity.String()} 78 | subjects = append(subjects, subject) 79 | } 80 | resp := &pb.SubjectResponse{Metadata: NewMetadata(sp.context.Host, data.Metadata.Id, false), 81 | Message: fmt.Sprintf("Subject response from %s", sp.context.Host.ID()), Subjects: subjects} 82 | 83 | // send the response 84 | ok := SendProtoMessage(sp.context.Host, s.Conn().RemotePeer(), subjectResponse, resp) 85 | if ok { 86 | utils.LogInfof("Subject response(%v) to %s sent.", subjects, s.Conn().RemotePeer().String()) 87 | } 88 | } 89 | 90 | // remote ping response handler 91 | func (sp *SubjectProtocol) onResponse(s network.Stream) { 92 | // results := make([]*subject.Subject, 0) 93 | 94 | data := &pb.SubjectResponse{} 95 | buf, err := ioutil.ReadAll(s) 96 | if err != nil { 97 | s.Reset() 98 | utils.LogErrorf("%v", err) 99 | return 100 | } 101 | s.Close() 102 | 103 | // unmarshal it 104 | proto.Unmarshal(buf, data) 105 | if err != nil { 106 | utils.LogErrorf("%v", err) 107 | return 108 | } 109 | 110 | defer func() { 111 | err := recover() 112 | if err != nil { 113 | utils.LogWarningf("panic: %v", err) 114 | } 115 | }() 116 | // Store all topics] 117 | var results []string 118 | for _, sub := range data.Subjects { 119 | identity := identity.NewIdentity(sub.Proposer) 120 | subject := subject.NewSubject(sub.Title, sub.Description, identity) 121 | 122 | b, err := json.Marshal(subject) 123 | if err != nil { 124 | utils.LogWarningf("Marshal failed, %v", err) 125 | } 126 | results = append(results, string(b)) 127 | } 128 | sp.channel[s.Conn().RemotePeer()] <- results 129 | 130 | // locate request data and remove it if found 131 | _, ok := sp.requests[data.Metadata.Id] 132 | if ok { 133 | // remove request from map as we have processed it here 134 | delete(sp.requests, data.Metadata.Id) 135 | } else { 136 | utils.LogWarning("Failed to locate request data boject for response") 137 | return 138 | } 139 | utils.LogInfof("Received subject response from %s. Message id:%s. Message: %s.", s.Conn().RemotePeer(), data.Metadata.Id, data.Message) 140 | 141 | } 142 | 143 | // SubmitRequest ... 144 | // TODO: use callback instead of channel 145 | func (sp *SubjectProtocol) SubmitRequest(peerID peer.ID, subjectHash *subject.Hash, ch chan<- []string) bool { 146 | utils.LogInfof("Sending subject request to: %s....", peerID) 147 | _ = subjectHash 148 | 149 | // create message data 150 | req := &pb.SubjectRequest{Metadata: NewMetadata(sp.context.Host, uuid.New().String(), false), 151 | Message: fmt.Sprintf("Subject request from %s", sp.context.Host.ID())} 152 | 153 | ok := SendProtoMessage(sp.context.Host, peerID, subjectRequest, req) 154 | if !ok { 155 | return false 156 | } 157 | 158 | sp.channel[peerID] = ch 159 | // store ref request so response handler has access to it 160 | sp.requests[req.Metadata.Id] = req 161 | // utils.LogInfof("Subject request to: %s was sent. Message Id: %s, Message: %s", peerID, req.Metadata.Id, req.Message) 162 | return true 163 | } 164 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/voter/identity_pool.go: -------------------------------------------------------------------------------- 1 | package voter 2 | 3 | import ( 4 | . "github.com/unitychain/zkvote-node/zkvote/model/identity" 5 | ) 6 | 7 | // IdentityPool ... 8 | type IdentityPool struct { 9 | rootHistory []*TreeContent 10 | tree *MerkleTree 11 | } 12 | 13 | const TREE_LEVEL uint8 = 10 14 | 15 | // NewIdentityPool ... 16 | func NewIdentityPool() (*IdentityPool, error) { 17 | return NewIdentityPoolWithTreeLevel(TREE_LEVEL) 18 | } 19 | 20 | // NewIdentityPoolWithTreeLevel ... 21 | func NewIdentityPoolWithTreeLevel(treeLevel uint8) (*IdentityPool, error) { 22 | tree, err := NewMerkleTree(treeLevel) 23 | if err != nil { 24 | return nil, err 25 | } 26 | rootHistory := []*TreeContent{tree.GetRoot()} 27 | 28 | return &IdentityPool{ 29 | rootHistory: rootHistory, 30 | tree: tree, 31 | }, nil 32 | } 33 | 34 | // InsertIdc : register id 35 | func (i *IdentityPool) InsertIdc(idCommitment *IdPathElement) (int, error) { 36 | c := idCommitment.Content() 37 | idx, err := i.tree.Insert(c) 38 | if err != nil { 39 | return -1, err 40 | } 41 | i.appendRoot(i.tree.GetRoot()) 42 | 43 | return idx, nil 44 | } 45 | 46 | // OverwriteIdElements . 47 | // return total len and error 48 | func (i *IdentityPool) OverwriteIdElements(commitmentSet []*IdPathElement) (int, error) { 49 | 50 | // backup tree and history 51 | bckTree := i.tree 52 | bckRootHistory := i.rootHistory 53 | 54 | // Iniitalize a new merkle tree 55 | tree, err := NewMerkleTree(TREE_LEVEL) 56 | if err != nil { 57 | return i.tree.Len(), err 58 | } 59 | rootHistory := []*TreeContent{tree.GetRoot()} 60 | i.tree = tree 61 | i.rootHistory = rootHistory 62 | 63 | // insert values to the new merkle tree 64 | var idx int = 0 65 | for _, e := range commitmentSet { 66 | idx, err = i.InsertIdc(e) 67 | if err != nil { 68 | break 69 | } 70 | } 71 | 72 | // error handling, recovery 73 | if err != nil { 74 | i.tree = bckTree 75 | i.rootHistory = bckRootHistory 76 | return i.tree.Len(), err 77 | } 78 | 79 | return idx + 1, nil 80 | } 81 | 82 | // Update : update id 83 | func (i *IdentityPool) Update(index uint, oldIDCommitment, newIDCommitment *IdPathElement) error { 84 | old, new := oldIDCommitment.Content(), newIDCommitment.Content() 85 | err := i.tree.Update(index, old, new) 86 | if err != nil { 87 | return err 88 | } 89 | i.appendRoot(i.tree.GetRoot()) 90 | 91 | return nil 92 | } 93 | 94 | // IsMember : check if the merkle root is in the root list or not 95 | func (i *IdentityPool) IsMember(root *IdPathElement) bool { 96 | for _, r := range i.rootHistory { 97 | if b, _ := r.Equals(root.Content()); b { 98 | return true 99 | } 100 | } 101 | return false 102 | } 103 | 104 | // HasRegistered . 105 | func (i *IdentityPool) HasRegistered(idc *IdPathElement) bool { 106 | c := idc.Content() 107 | return i.tree.IsExisted(&c) 108 | } 109 | 110 | // GetAllIds . 111 | func (i *IdentityPool) GetAllIds() []*IdPathElement { 112 | treeContents := i.tree.GetAllContent() 113 | elements := make([]*IdPathElement, len(treeContents)) 114 | for i, c := range treeContents { 115 | elements[i] = NewIdPathElement(c) 116 | } 117 | return elements 118 | } 119 | 120 | // GetIndex . 121 | func (i *IdentityPool) GetIndex(value *IdPathElement) int { 122 | c := value.Content() 123 | return i.tree.GetIndexByValue(&c) 124 | } 125 | 126 | // GetIdentityTreePath . 127 | func (i *IdentityPool) GetIdentityTreePath(value *IdPathElement) ([]*IdPathElement, []int, *IdPathElement) { 128 | c := value.Content() 129 | inters, interIdxs, root := i.tree.GetIntermediateValues(&c) 130 | elements := make([]*IdPathElement, len(inters)) 131 | for i, c := range inters { 132 | elements[i] = NewIdPathElement(c) 133 | } 134 | return elements, interIdxs, NewIdPathElement(root) 135 | } 136 | 137 | // 138 | // Internal functions 139 | // 140 | func (i *IdentityPool) appendRoot(r *TreeContent) { 141 | i.rootHistory = append(i.rootHistory, i.tree.GetRoot()) 142 | } 143 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/voter/identity_pool_test.go: -------------------------------------------------------------------------------- 1 | package voter 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "testing" 7 | 8 | . "github.com/unitychain/zkvote-node/zkvote/model/identity" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | const idCommitment string = "17610192990552485611214212559447309706539616482639833145108503521837267798810" 14 | 15 | func TestRegister(t *testing.T) { 16 | id, err := NewIdentityPoolWithTreeLevel(10) 17 | assert.Nil(t, err, "new identity instance error") 18 | 19 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 20 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 21 | assert.Nil(t, err, "register error") 22 | assert.Equal(t, 0, idx) 23 | 24 | expectedRoot, _ := big.NewInt(0).SetString("1603056697422863699573935817849018482475219731925672640724433076363786113", 10) 25 | assert.Equal(t, expectedRoot, id.tree.GetRoot().BigInt()) 26 | } 27 | 28 | func TestRegister_10IDs(t *testing.T) { 29 | id, err := NewIdentityPoolWithTreeLevel(10) 30 | assert.Nil(t, err, "new identity instance error") 31 | 32 | for i := 0; i < 10; i++ { 33 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 34 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 35 | assert.Nil(t, err, "register error") 36 | assert.Equal(t, i, idx) 37 | } 38 | } 39 | 40 | func TestRegister_Double(t *testing.T) { 41 | id, err := NewIdentityPoolWithTreeLevel(10) 42 | assert.Nil(t, err, "new identity instance error") 43 | 44 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 45 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 46 | assert.Nil(t, err, "register error") 47 | assert.Equal(t, 0, idx) 48 | 49 | idx, err = id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 50 | assert.NotNil(t, err, "should not register successfully") 51 | } 52 | 53 | func TestUpdate(t *testing.T) { 54 | id, err := NewIdentityPoolWithTreeLevel(10) 55 | assert.Nil(t, err, "new identity instance error") 56 | 57 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 58 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 59 | assert.Nil(t, err, "register error") 60 | assert.Equal(t, 0, idx) 61 | 62 | err = id.Update(uint(idx), NewIdPathElement(NewTreeContent(idc)), NewIdPathElement(NewTreeContent(big.NewInt(100)))) 63 | assert.Nil(t, err, "update error") 64 | } 65 | 66 | func TestUpdate_IncorrectIdx(t *testing.T) { 67 | id, err := NewIdentityPoolWithTreeLevel(10) 68 | assert.Nil(t, err, "new identity instance error") 69 | 70 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 71 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 72 | assert.Nil(t, err, "register error") 73 | assert.Equal(t, 0, idx) 74 | 75 | err = id.Update(1, NewIdPathElement(NewTreeContent(idc)), NewIdPathElement(NewTreeContent(big.NewInt(100)))) 76 | assert.NotNil(t, err, "update error") 77 | } 78 | 79 | func TestUpdate_IncorrectContent(t *testing.T) { 80 | id, err := NewIdentityPoolWithTreeLevel(10) 81 | assert.Nil(t, err, "new identity instance error") 82 | 83 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 84 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 85 | assert.Nil(t, err, "register error") 86 | assert.Equal(t, 0, idx) 87 | 88 | err = id.Update(uint(idx), NewIdPathElement(NewTreeContent(big.NewInt(100))), NewIdPathElement(NewTreeContent(big.NewInt(100)))) 89 | assert.NotNil(t, err, "update error") 90 | } 91 | 92 | func TestIsMember(t *testing.T) { 93 | id, err := NewIdentityPoolWithTreeLevel(10) 94 | assert.Nil(t, err, "new identity instance error") 95 | 96 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 97 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 98 | assert.Nil(t, err, "register error") 99 | assert.Equal(t, 0, idx) 100 | 101 | assert.True(t, id.IsMember(NewIdPathElement(id.tree.GetRoot()))) 102 | } 103 | 104 | func TestIsMember2(t *testing.T) { 105 | id, err := NewIdentityPoolWithTreeLevel(10) 106 | assert.Nil(t, err, "new identity instance error") 107 | 108 | idc, _ := big.NewInt(0).SetString(idCommitment, 10) 109 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 110 | assert.Nil(t, err, "register error") 111 | assert.Equal(t, 0, idx) 112 | root1 := id.tree.GetRoot() 113 | 114 | idx, err = id.InsertIdc(NewIdPathElement(NewTreeContent(big.NewInt(10)))) 115 | assert.Nil(t, err, "register error") 116 | assert.Equal(t, 1, idx) 117 | 118 | assert.True(t, id.IsMember(NewIdPathElement(root1))) 119 | assert.True(t, id.IsMember(NewIdPathElement(id.tree.GetRoot()))) 120 | } 121 | 122 | func TestOverwrite(t *testing.T) { 123 | id, err := NewIdentityPoolWithTreeLevel(10) 124 | assert.Nil(t, err, "new identity instance error") 125 | 126 | for i := 0; i < 3; i++ { 127 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 128 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 129 | assert.Nil(t, err, "register error") 130 | assert.Equal(t, i, idx) 131 | } 132 | 133 | commitmentSet := make([]*IdPathElement, 10) 134 | for i := 0; i < 10; i++ { 135 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 136 | commitmentSet[i] = NewIdPathElement(NewTreeContent(idc)) 137 | } 138 | 139 | num, err := id.Overwrite(commitmentSet) 140 | assert.Nil(t, err, "overwrite error") 141 | assert.Equal(t, 10, num) 142 | } 143 | func TestOverwrite2(t *testing.T) { 144 | id, err := NewIdentityPoolWithTreeLevel(10) 145 | assert.Nil(t, err, "new identity instance error") 146 | 147 | for i := 0; i < 10; i++ { 148 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 149 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 150 | assert.Nil(t, err, "register error") 151 | assert.Equal(t, i, idx) 152 | } 153 | 154 | commitmentSet := make([]*IdPathElement, 3) 155 | for i := 0; i < 3; i++ { 156 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 157 | commitmentSet[i] = NewIdPathElement(NewTreeContent(idc)) 158 | } 159 | 160 | num, err := id.Overwrite(commitmentSet) 161 | assert.Nil(t, err, "overwrite error") 162 | assert.Equal(t, 3, num) 163 | } 164 | 165 | func TestOverwrite_ForceError(t *testing.T) { 166 | id, err := NewIdentityPoolWithTreeLevel(10) 167 | assert.Nil(t, err, "new identity instance error") 168 | 169 | for i := 0; i < 3; i++ { 170 | idc, _ := big.NewInt(0).SetString(fmt.Sprintf("%d", 100*i+1), 10) 171 | idx, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 172 | assert.Nil(t, err, "register error") 173 | assert.Equal(t, i, idx) 174 | } 175 | 176 | commitmentSet := make([]*IdPathElement, 10) 177 | for i := 0; i < 10; i++ { 178 | commitmentSet[i] = NewIdPathElement(NewTreeContent(big.NewInt(0))) 179 | } 180 | 181 | num, err := id.Overwrite(commitmentSet) 182 | assert.NotNil(t, err, "overwrite should have errors") 183 | assert.Equal(t, 3, num) 184 | } 185 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/voter/proposal.go: -------------------------------------------------------------------------------- 1 | package voter 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | crypto "github.com/ethereum/go-ethereum/crypto" 8 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 9 | ba "github.com/unitychain/zkvote-node/zkvote/model/ballot" 10 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 11 | "github.com/unitychain/zkvote-node/zkvote/snark" 12 | ) 13 | 14 | type state struct { 15 | records []*big.Int 16 | opinion []bool 17 | finished bool 18 | } 19 | 20 | type nullifier struct { 21 | content string 22 | hash *big.Int 23 | voteState state 24 | } 25 | 26 | // Proposal ... 27 | // TODO: Rename 28 | type Proposal struct { 29 | nullifiers map[int]*nullifier 30 | ballotMap ba.Map 31 | index int 32 | } 33 | 34 | const HASH_YES = "43379584054787486383572605962602545002668015983485933488536749112829893476306" 35 | const HASH_NO = "85131057757245807317576516368191972321038229705283732634690444270750521936266" 36 | 37 | // NewProposal ... 38 | func NewProposal() (*Proposal, error) { 39 | nullifiers := map[int]*nullifier{ 40 | 0: &nullifier{ 41 | hash: big.NewInt(0).SetBytes(crypto.Keccak256([]byte("empty"))), 42 | content: "empty", 43 | voteState: state{ 44 | records: []*big.Int{}, 45 | opinion: []bool{}, 46 | finished: false, 47 | }, 48 | }} 49 | index := 0 50 | 51 | return &Proposal{ 52 | nullifiers: nullifiers, 53 | ballotMap: ba.NewMap(), 54 | index: index, 55 | }, nil 56 | } 57 | 58 | // Propose : propose the hash of a question 59 | func (p *Proposal) ProposeSubject(subHash subject.HashHex) int { 60 | if 0 == len(subHash) { 61 | utils.LogWarning("input qeustion in empty") 62 | return -1 63 | } 64 | 65 | // bigHashQus := big.NewInt(0).SetBytes(crypto.Keccak256([]byte(q))) 66 | bigHashQus := utils.GetBigIntFromHexString(subHash.String()) 67 | p.nullifiers[p.index] = &nullifier{ 68 | // TODO: Div(8) is a workaround because a bits conversion issue in circom 69 | hash: bigHashQus.Div(bigHashQus, big.NewInt(8)), 70 | content: subHash.String(), 71 | voteState: state{ 72 | opinion: []bool{}, 73 | finished: false, 74 | }, 75 | } 76 | p.index++ 77 | 78 | return p.index - 1 79 | } 80 | 81 | // VoteWithProof : vote with zk proof 82 | func (p *Proposal) VoteWithProof(ballot *ba.Ballot, vkString string) error { 83 | if b, e := p.isValidVote(ballot, vkString); !b { 84 | return e 85 | } 86 | 87 | bigNullHash, _ := big.NewInt(0).SetString(ballot.NullifierHash, 10) 88 | p.nullifiers[0].voteState.records = append(p.nullifiers[0].voteState.records, bigNullHash) 89 | 90 | if ballot.PublicSignal[2] == HASH_YES { 91 | p.nullifiers[0].voteState.opinion = append(p.nullifiers[0].voteState.opinion, true) 92 | } else { 93 | p.nullifiers[0].voteState.opinion = append(p.nullifiers[0].voteState.opinion, false) 94 | } 95 | 96 | p.ballotMap[ballot.NullifierHashHex()] = ballot 97 | return nil 98 | } 99 | 100 | // Remove : remove a proposal from the list 101 | func (p *Proposal) Remove(idx int) { 102 | if !p.checkIndex(idx) { 103 | return 104 | } 105 | delete(p.nullifiers, idx) 106 | } 107 | 108 | // Close : close a proposal which means can't vote anymore 109 | func (p *Proposal) Close(idx int) { 110 | if !p.checkIndex(idx) { 111 | return 112 | } 113 | p.nullifiers[idx].voteState.finished = true 114 | } 115 | 116 | // InsertBallot ... 117 | func (p *Proposal) InsertBallot(ballot *ba.Ballot) error { 118 | if nil == ballot { 119 | return fmt.Errorf("invalid input") 120 | } 121 | 122 | p.ballotMap[ballot.NullifierHashHex()] = ballot 123 | return nil 124 | } 125 | 126 | func (p *Proposal) GetBallots() ba.Map { 127 | return p.ballotMap 128 | } 129 | 130 | // HasProposal : check proposal exists or not 131 | // return : -1, not exists, proposal index otherwise 132 | func (p *Proposal) HasProposal(q string) int { 133 | for i, e := range p.nullifiers { 134 | if e.content == q { 135 | return i 136 | } 137 | } 138 | return -1 139 | } 140 | 141 | // HasProposalByHash : check proposal exists or not 142 | // return : -1, not exists, proposal index otherwise 143 | func (p *Proposal) HasProposalByHash(hash *big.Int) int { 144 | for i, e := range p.nullifiers { 145 | if 0 == e.hash.Cmp(hash) { 146 | return i 147 | } 148 | } 149 | return -1 150 | } 151 | 152 | // GetCurrentIdex : get current index of whole questions 153 | func (p *Proposal) GetCurrentIdex() int { 154 | return p.index 155 | } 156 | 157 | // GetVotes : get total votes 158 | func (p *Proposal) GetVotes(idx int) (yes, no int) { 159 | nul := p.getProposal(idx) 160 | if nul == nil { 161 | return -1, -1 162 | } 163 | ops := nul.voteState.opinion 164 | 165 | for _, o := range ops { 166 | if true == o { 167 | yes++ 168 | } else { 169 | no++ 170 | } 171 | } 172 | return yes, no 173 | } 174 | 175 | // GetProposal : Get a proposal instance 176 | func (p *Proposal) getProposal(idx int) *nullifier { 177 | if !p.checkIndex(idx) { 178 | return nil 179 | } 180 | return p.nullifiers[idx] 181 | } 182 | 183 | // // GetProposalByHash : Get a proposal instance by question hash 184 | // func (p *Proposal) getProposalByHash(hash *big.Int) *nullifier { 185 | // idx := p.HasProposalByHash(hash) 186 | // return p.getProposal(idx) 187 | // } 188 | 189 | // 190 | // Internal functions 191 | // 192 | func (p *Proposal) isFinished() bool { 193 | return p.nullifiers[0].voteState.finished 194 | } 195 | 196 | func (p *Proposal) isValidVote(ballot *ba.Ballot, vkString string) (bool, error) { 197 | if 0 == len(vkString) { 198 | utils.LogWarningf("invalid input: %s", vkString) 199 | return false, fmt.Errorf("vk string is empty") 200 | } 201 | if p.isFinished() { 202 | utils.LogWarningf("this question has been closed") 203 | return false, fmt.Errorf("this question has been closed") 204 | } 205 | 206 | nullifierHash := ballot.PublicSignal[1] 207 | singalHash := ballot.PublicSignal[2] 208 | externalNullifier := ballot.PublicSignal[3] 209 | 210 | bigExternalNull, _ := big.NewInt(0).SetString(externalNullifier, 10) 211 | if 0 != p.nullifiers[0].hash.Cmp(bigExternalNull) { 212 | utils.LogWarningf("question doesn't match (%v)/(%v)", p.nullifiers[0].hash, bigExternalNull) 213 | return false, fmt.Errorf(fmt.Sprintf("question doesn't match (%v)/(%v)", p.nullifiers[0].hash, bigExternalNull)) 214 | } 215 | if p.isVoted(nullifierHash) { 216 | utils.LogWarningf("Voted already, %v", nullifierHash) 217 | return false, fmt.Errorf("voted already") 218 | } 219 | if !isValidOpinion(singalHash) { 220 | utils.LogWarningf("Not a valid vote hash, %v", singalHash) 221 | return false, fmt.Errorf(fmt.Sprintf("Not a valid vote hash, %v", singalHash)) 222 | } 223 | 224 | return snark.Verify(vkString, ballot.Proof, ballot.PublicSignal), nil 225 | } 226 | 227 | func (p *Proposal) isVoted(nullifierHash string) bool { 228 | bigNullHash, _ := big.NewInt(0).SetString(nullifierHash, 10) 229 | for _, r := range p.nullifiers[0].voteState.records { 230 | if 0 == bigNullHash.Cmp(r) { 231 | return true 232 | } 233 | } 234 | return false 235 | } 236 | 237 | func isValidOpinion(hash string) bool { 238 | if hash == HASH_NO || hash == HASH_YES { 239 | return true 240 | } 241 | return false 242 | } 243 | 244 | func (p *Proposal) checkIndex(idx int) bool { 245 | if 0 > idx { 246 | utils.LogWarningf("invalid index, %d", idx) 247 | return false 248 | } 249 | if idx > p.GetCurrentIdex() { 250 | utils.LogWarningf("index (%d) is incorrect, max index is %d", idx, p.index) 251 | return false 252 | } 253 | if nil == p.nullifiers[idx] { 254 | utils.LogWarningf("question doesn't exist (index %d)", idx) 255 | return false 256 | } 257 | return true 258 | } 259 | -------------------------------------------------------------------------------- /zkvote/operator/service/manager/voter/voter.go: -------------------------------------------------------------------------------- 1 | package voter 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | pubsub "github.com/libp2p/go-libp2p-pubsub" 8 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 9 | ba "github.com/unitychain/zkvote-node/zkvote/model/ballot" 10 | localContext "github.com/unitychain/zkvote-node/zkvote/model/context" 11 | id "github.com/unitychain/zkvote-node/zkvote/model/identity" 12 | "github.com/unitychain/zkvote-node/zkvote/model/subject" 13 | ) 14 | 15 | type voterSubscription struct { 16 | idSub *pubsub.Subscription 17 | voteSub *pubsub.Subscription 18 | } 19 | 20 | // Voter . 21 | type Voter struct { 22 | subject *subject.Subject 23 | *IdentityPool 24 | *Proposal 25 | verificationKey string 26 | 27 | *localContext.Context 28 | ps *pubsub.PubSub 29 | subscription *voterSubscription 30 | pubMsg map[string][]*pubsub.Message 31 | } 32 | 33 | // NewVoter ... 34 | func NewVoter( 35 | subject *subject.Subject, 36 | ps *pubsub.PubSub, 37 | lc *localContext.Context, 38 | verificationKey string, 39 | ) (*Voter, error) { 40 | id, err := NewIdentityPool() 41 | if nil != err { 42 | return nil, err 43 | } 44 | p, err := NewProposal() 45 | if nil != err { 46 | return nil, err 47 | } 48 | 49 | identitySub, err := ps.Subscribe("identity/" + subject.HashHex().String()) 50 | if err != nil { 51 | return nil, err 52 | } 53 | voteSub, err := ps.Subscribe("vote/" + subject.HashHex().String()) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | v := &Voter{ 59 | subject: subject, 60 | IdentityPool: id, 61 | Proposal: p, 62 | ps: ps, 63 | Context: lc, 64 | verificationKey: verificationKey, 65 | subscription: &voterSubscription{ 66 | idSub: identitySub, 67 | voteSub: voteSub, 68 | }, 69 | } 70 | v.Propose() 71 | 72 | go v.identitySubHandler(v.subject.Hash(), v.subscription.idSub) 73 | go v.voteSubHandler(v.subscription.voteSub) 74 | 75 | return v, nil 76 | } 77 | 78 | // 79 | // Identities 80 | // 81 | 82 | // InsertIdentity . 83 | func (v *Voter) InsertIdentity(identity *id.Identity, publish bool) (int, error) { 84 | if nil == identity { 85 | return -1, fmt.Errorf("invalid input") 86 | } 87 | 88 | i, err := v.InsertIdc(identity.PathElement()) 89 | if nil != err { 90 | return -1, err 91 | } 92 | 93 | v.Cache.InsertIdentity(v.subject.Hash().Hex(), *identity) 94 | 95 | if publish { 96 | return i, v.ps.Publish(v.GetIdentitySub().Topic(), identity.Byte()) 97 | } 98 | 99 | return i, nil 100 | } 101 | 102 | // Join . 103 | // func (v *Voter) Join(identity *id.Identity) error { 104 | // return v.ps.Publish(v.GetIdentitySub().Topic(), identity.Byte()) 105 | // } 106 | 107 | // OverwriteIds . 108 | func (v *Voter) OverwriteIds(identities []*id.Identity) (int, error) { 109 | idElements := make([]*id.IdPathElement, len(identities)) 110 | for i, e := range identities { 111 | idElements[i] = e.PathElement() 112 | v.Cache.InsertIdentity(v.subject.Hash().Hex(), *e) 113 | } 114 | 115 | return v.OverwriteIdElements(idElements) 116 | } 117 | 118 | // 119 | // Proposal 120 | // 121 | // Vote . 122 | func (v *Voter) Vote(ballot *ba.Ballot, silent bool) error { 123 | bytes, err := ballot.Byte() 124 | if err != nil { 125 | return err 126 | } 127 | 128 | // Check membership 129 | bigRoot, _ := big.NewInt(0).SetString(ballot.Root, 10) 130 | if !v.IsMember(id.NewIdPathElement(id.NewTreeContent(bigRoot))) { 131 | return fmt.Errorf("Not a member") 132 | } 133 | 134 | // Update voteState 135 | err = v.VoteWithProof(ballot, v.verificationKey) 136 | if err != nil { 137 | return err 138 | } 139 | 140 | v.Context.Cache.InsertBallot(v.subject.Hash().Hex(), ballot) 141 | 142 | if !silent { 143 | return v.ps.Publish(v.GetVoteSub().Topic(), bytes) 144 | } 145 | return nil 146 | } 147 | 148 | // Open . 149 | func (v *Voter) Open() (yes, no int) { 150 | return v.GetVotes(0) 151 | } 152 | 153 | // Propose . 154 | func (v *Voter) Propose() int { 155 | return v.ProposeSubject(*v.subject.HashHex()) 156 | } 157 | 158 | // 159 | // Getters 160 | // 161 | 162 | // GetIdentityIndex . 163 | func (v *Voter) GetIdentityIndex(identity id.Identity) int { 164 | return v.GetIndex(identity.PathElement()) 165 | } 166 | 167 | // GetSubject . 168 | func (v *Voter) GetSubject() *subject.Subject { 169 | return v.subject 170 | } 171 | 172 | // GetIdentitySub ... 173 | func (v *Voter) GetIdentitySub() *pubsub.Subscription { 174 | return v.subscription.idSub 175 | } 176 | 177 | // GetVoteSub ... 178 | func (v *Voter) GetVoteSub() *pubsub.Subscription { 179 | return v.subscription.voteSub 180 | } 181 | 182 | // GetBallotMap ... 183 | func (v *Voter) GetBallotMap() ba.Map { 184 | return v.GetBallots() 185 | } 186 | 187 | // GetAllIdentities . 188 | func (v *Voter) GetAllIdentities() []id.Identity { 189 | ids := v.GetAllIds() 190 | hexArray := make([]id.Identity, len(ids)) 191 | for _, _id := range ids { 192 | hexArray = append(hexArray, *id.NewIdentity(_id.Hex())) 193 | } 194 | return hexArray 195 | } 196 | 197 | // GetIdentityPath . 198 | func (v *Voter) GetIdentityPath(identity id.Identity) ([]*id.IdPathElement, []int, *id.IdPathElement, error) { 199 | elements, paths, root := v.GetIdentityTreePath(identity.PathElement()) 200 | if nil == paths { 201 | return nil, nil, nil, fmt.Errorf("Can't find the element") 202 | } 203 | return elements, paths, root, nil 204 | } 205 | 206 | // 207 | // internals 208 | // 209 | 210 | func (v *Voter) identitySubHandler(subjectHash *subject.Hash, subscription *pubsub.Subscription) { 211 | for { 212 | m, err := subscription.Next(*v.Ctx) 213 | if err != nil { 214 | utils.LogErrorf("Failed to get identity subscription, %v", err.Error()) 215 | continue 216 | } 217 | utils.LogDebugf("identitySubHandler: Received message") 218 | 219 | // TODO: Same logic as Register 220 | identity := id.NewIdentityFromBytes(m.GetData()) 221 | if v.HasRegistered(identity.PathElement()) { 222 | utils.LogInfof("Got registed id commitment, %v", identity.String()) 223 | continue 224 | } 225 | 226 | // TODO: Implement consensus for insert 227 | _, err = v.InsertIdentity(identity, false) 228 | if nil != err { 229 | utils.LogWarningf("Insert id from pubsub error, %v", err.Error()) 230 | continue 231 | } 232 | } 233 | } 234 | 235 | func (v *Voter) voteSubHandler(sub *pubsub.Subscription) { 236 | for { 237 | m, err := sub.Next(*v.Ctx) 238 | if err != nil { 239 | utils.LogErrorf("Failed to get vote subscription, %v", err.Error()) 240 | continue 241 | } 242 | utils.LogDebugf("voteSubHandler: Received message") 243 | 244 | // Get Ballot 245 | ballotStr := string(m.GetData()) 246 | ballot, err := ba.NewBallot(ballotStr) 247 | if err != nil { 248 | utils.LogWarningf("voteSubHandler: %v", err.Error()) 249 | continue 250 | } 251 | 252 | // TODO: Same logic as Vote 253 | // Check membership 254 | bigRoot, _ := big.NewInt(0).SetString(ballot.Root, 10) 255 | if !v.IsMember(id.NewIdPathElement(id.NewTreeContent(bigRoot))) { 256 | err = fmt.Errorf("Not a member") 257 | utils.LogWarningf("voteSubHandler: %v, %v", err.Error(), bigRoot) 258 | continue 259 | } 260 | 261 | // Update voteState 262 | err = v.VoteWithProof(ballot, v.verificationKey) 263 | if err != nil { 264 | utils.LogWarningf("voteSubHandler: %v", err.Error()) 265 | continue 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /zkvote/operator/service/test/snark_verifier_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "math/big" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | . "github.com/unitychain/zkvote-node/zkvote/model/identity" 11 | "github.com/unitychain/zkvote-node/zkvote/operator/service/manager/voter" 12 | ) 13 | 14 | var idCommitment = [...]string{ 15 | "17610192990552485611214212559447309706539616482639833145108503521837267798810", 16 | "12911218845103750861406388355446622324232020446592892913783900846357287231484", 17 | "1032284940727177649939355598892149604970527830516654273792409518693495635491", 18 | "21176767283001926398440783773513762000716980892317227378236556477014800668400", 19 | "277593402026800763958336343436402082617452946079134938418951384937122081698", 20 | "6894328305305102806720810244282194911715263103973532827353421862495894269552", 21 | "16696521640980513762895570995527758356630582039153470423682046528515430261262", 22 | "3445457589949194301656936787185600418234571893114183450807142773072595456469", 23 | "312527539520761448694291090800047775034752705203482547162969903432178197468", 24 | "15339060964594858963739198162689168732368415427742964439329199487013648959013", 25 | } 26 | 27 | func TestRegister(t *testing.T) { 28 | id, _ := voter.NewIdentityPool() 29 | // New proposal 30 | p, _ := voter.NewProposal(id) 31 | qIdx := p.Propose("this is a question.") 32 | vkData, err := ioutil.ReadFile("../snark/verification_key.json") 33 | assert.Nil(t, err) 34 | 35 | y, n := 0, 0 36 | for i, s := range idCommitment { 37 | fmt.Println("") 38 | idc, _ := big.NewInt(0).SetString(s, 10) 39 | _, err := id.InsertIdc(NewIdPathElement(NewTreeContent(idc))) 40 | assert.Nil(t, err) 41 | 42 | // submit proof 43 | dat, err := ioutil.ReadFile(fmt.Sprintf("./vectors/vote%d.proof", i)) 44 | assert.Nil(t, err) 45 | 46 | err = p.VoteWithProof(qIdx, string(dat), string(vkData)) 47 | assert.Nil(t, err) 48 | 49 | y, n = p.GetVotes(qIdx) 50 | } 51 | assert.Equal(t, 5, y) 52 | assert.Equal(t, 5, n) 53 | } 54 | -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote0.proof: -------------------------------------------------------------------------------- 1 | {"root":"1603056697422863699573935817849018482475219731925672640724433076363786113","nullifier_hash":"9209935233086134651429342564066540372010003423825221251623438078324132703005","proof":{"pi_a":["13862018769176408654557878303258422977372570156005950906750594914905072002550","4683975830196691695306757075679551580096470187515470380016494066127874235242","1"],"pi_b":[["13453527910197148610782466478305274651058561032671752335650864014243061704434","18645189788145486135271375816065576109987315148200112683784638537012650254313"],["7734517262166638900371489950583935871298182578768415897690240336787394743748","17921050622246768150421692105356526988744674913910765693395209944024270764742"],["1","0"]],"pi_c":["10453716040370598269972525081195272028544145906517562336151947320924833345260","5790724294985261956879165436587583053935971877177314513395693636995449909160","1"],"protocol":"groth"},"public_signal":["1603056697422863699573935817849018482475219731925672640724433076363786113","9209935233086134651429342564066540372010003423825221251623438078324132703005","85131057757245807317576516368191972321038229705283732634690444270750521936266","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote1.proof: -------------------------------------------------------------------------------- 1 | {"root":"9508927468061241572326004439201823909996138319398271249258174122714640592162","nullifier_hash":"9397702578200711155677712987903663176920090462855768887995593686802240223964","proof":{"pi_a":["5130864991510516020589301482816385045114505165388788331277364659354435182810","14624787538719710071561135714681794820435278427338354053426473370895245586473","1"],"pi_b":[["2519751358858314680632043259616681530636461459914595622833863468231886154181","14851006424341806526548437698456612289914012850227010536650813352300674130470"],["19829824757938655441339463777969715228589298658301180511998154963361744586767","20709947025569446219123821817494267516151918988747204458454289801481828597513"],["1","0"]],"pi_c":["15724995963652449382424287839617233008320513085948692491849187549005477915174","19274913245849688437675507118686581038440252759771900156316348927882003233743","1"],"protocol":"groth"},"public_signal":["9508927468061241572326004439201823909996138319398271249258174122714640592162","9397702578200711155677712987903663176920090462855768887995593686802240223964","43379584054787486383572605962602545002668015983485933488536749112829893476306","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote2.proof: -------------------------------------------------------------------------------- 1 | {"root":"16061129643151521043608931733674164133121735478779353591821478198276200109767","nullifier_hash":"8405072435488454961223679009752481064266488895161653858568543267403389899404","proof":{"pi_a":["12540191305175973108072858451846361527934911724193212650964881515652381678050","11158408418376698398506976321287375007014654284776209399765054567731217744590","1"],"pi_b":[["15906322193569928810329269613576705306308146224434083584118132581109139326233","8162929042887705137059656911937439436280696342110921410573968876822774244798"],["10692511506307870163697682143530882843873980785888507143869415547462902847797","11937373117999774624001778249847387283742313245762743016081813393998544851069"],["1","0"]],"pi_c":["4599659394807279485664640793634882876612709907910021662792224037773805316006","20355598280711392166276703780312451119586318391688364135178105775692673379363","1"],"protocol":"groth"},"public_signal":["16061129643151521043608931733674164133121735478779353591821478198276200109767","8405072435488454961223679009752481064266488895161653858568543267403389899404","85131057757245807317576516368191972321038229705283732634690444270750521936266","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote3.proof: -------------------------------------------------------------------------------- 1 | {"root":"17095996690199260311731543307042340615160390839020859566677325379725229764818","nullifier_hash":"12217921542974940565417800826750850720428906157057505789768871524751057081995","proof":{"pi_a":["5721677036907250630980083701051668839938194565053994210909387664066313942058","3613861963564065702779285767135234771137677716841510972431482163594575616092","1"],"pi_b":[["15517264169133340650392390838356096418028058620019927764474594739270594204655","672188133187285889687593086576288115578476981336659010391811936075082670010"],["15517499991078844583285253745764401676463024862339084976288961250948686701940","8996003195563839530785963613379647823204088061913175769537148965131748137429"],["1","0"]],"pi_c":["6557777926925847974759236174853947132071193495283105638201283951471579871774","20849238342181001859431307770784431682807985526611087834855054228606479903140","1"],"protocol":"groth"},"public_signal":["17095996690199260311731543307042340615160390839020859566677325379725229764818","12217921542974940565417800826750850720428906157057505789768871524751057081995","43379584054787486383572605962602545002668015983485933488536749112829893476306","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote4.proof: -------------------------------------------------------------------------------- 1 | {"root":"11293442735951324527006655784472166870373706742489575471982472813698515308508","nullifier_hash":"4395201880302905153216730713046610189433945449662572774707451975937789596788","proof":{"pi_a":["13105888094764236864344219420727461769496332743986747803965843556357248844304","12852526646181081252420662591399826884875104853075032249652542480123328972054","1"],"pi_b":[["10931497434673258270394439728561148974233558142828207249433420782702883078869","12510616923411793997864669824093254777125942893926124497143498514521316211151"],["13426455634142877984863261479299255867220202258732245292797196936149890326867","8754316668375462406749824107753484997556122612868756874097593226296295475635"],["1","0"]],"pi_c":["8499646611305585538210350546287397587111324807898638036286877446485039462197","9338036046494086872799550667146682928469343293003288350832498142562201447741","1"],"protocol":"groth"},"public_signal":["11293442735951324527006655784472166870373706742489575471982472813698515308508","4395201880302905153216730713046610189433945449662572774707451975937789596788","85131057757245807317576516368191972321038229705283732634690444270750521936266","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote5.proof: -------------------------------------------------------------------------------- 1 | {"root":"8810097828105195781026061348999003923878505542390686153451559916300845151532","nullifier_hash":"6175461400648683700753029342628788745721187349232991203023557842314001262929","proof":{"pi_a":["19703089076393209122733781524695777026310521128410311703345145284874780710925","17188016401264420997884457194877633090006578916296172031644986999038635958021","1"],"pi_b":[["4875650039850023671140544765343182424873577564540819254440192196754659939371","21514267416701464380268598736594896822290286976011702484238758332984671079753"],["15095209400692576261387634471008617905307929396742705473681456492962272932650","4909697222696169048475058813194719664254741091769449502252799596369768796697"],["1","0"]],"pi_c":["15546001805770828351569882953167172539949268784319874895280223895777499467293","13693093804873348786381764957849324829437787118989748179662588044323699506359","1"],"protocol":"groth"},"public_signal":["8810097828105195781026061348999003923878505542390686153451559916300845151532","6175461400648683700753029342628788745721187349232991203023557842314001262929","43379584054787486383572605962602545002668015983485933488536749112829893476306","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote6.proof: -------------------------------------------------------------------------------- 1 | {"root":"15964557573325002366356907628429444568088662264890204208171669734268243404665","nullifier_hash":"1746591142732877423319814169554068051311607195763029553018799361267506206535","proof":{"pi_a":["20939170080522454780809405457119342012778453360084045917268995629484420403126","14335947715813121567493974022567789746516018593237292014478282496338455228444","1"],"pi_b":[["1234234745281515069467124981759283688962938338569006852844586726677373131676","60056285870415135442083513441792803741059208062403499198845696767189347382"],["14390956058430769020129660484664062514413034232934772074864775258243316620697","20376202592131701687893681538933823501734916284896863519862290053019394775986"],["1","0"]],"pi_c":["3258774003834604262514297376096915605264813737854149342181769937237670519241","16946198879271095441014495556279315389460964526422915789395376113813257127165","1"],"protocol":"groth"},"public_signal":["15964557573325002366356907628429444568088662264890204208171669734268243404665","1746591142732877423319814169554068051311607195763029553018799361267506206535","85131057757245807317576516368191972321038229705283732634690444270750521936266","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote7.proof: -------------------------------------------------------------------------------- 1 | {"root":"13537404055536256139604079727498948534702954422355816630268867561323212371989","nullifier_hash":"92496873994139310055958451429696989814330515551129544023595662987789993838","proof":{"pi_a":["8044026898143462376148791704906846747040599177701906710746013474962842251163","14159596008263392988017068763595524453972523306769609761399158563307512606663","1"],"pi_b":[["3224715121533013514617420722411288746565456058876192543460573447659769032649","17319876600064562329966758991756928505204566729130823260227719042610911748848"],["9095938001346976702444543509517583788900244834331698963214314809971341419575","6182508612587076139041052419515295969426007400155479801197107795033336586408"],["1","0"]],"pi_c":["14340727315590995260973598499022501399139839768625450903636342538797478788697","19442809762870849393807883127749205405258951817801636620120373643239094609420","1"],"protocol":"groth"},"public_signal":["13537404055536256139604079727498948534702954422355816630268867561323212371989","92496873994139310055958451429696989814330515551129544023595662987789993838","43379584054787486383572605962602545002668015983485933488536749112829893476306","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote8.proof: -------------------------------------------------------------------------------- 1 | {"root":"1931644966272405050789794652671826880883278874134474227108111704821416369726","nullifier_hash":"6909482600823053520857952265783426995338928491417720318976174410621125881725","proof":{"pi_a":["20042777604462424903403539504481209730155800169211870219797714782791328731383","835777886639286143259753790128245161597215795404054659498610364608643491476","1"],"pi_b":[["19951233255372828871978758609811032293917632575395987364507878312934859998390","10822461195664511958999893819054547780549450087800356499517740365616003363828"],["21855686427966170006043929353779917518615424743888894999494351565883961035810","1511755681189223773721492610678071517490617266153013415780484773695333080857"],["1","0"]],"pi_c":["9094544234226958616601548231258355571442408089435004835717863901654586357383","2139851503785645551925808862234756679873455650766200019678471881248032998792","1"],"protocol":"groth"},"public_signal":["1931644966272405050789794652671826880883278874134474227108111704821416369726","6909482600823053520857952265783426995338928491417720318976174410621125881725","85131057757245807317576516368191972321038229705283732634690444270750521936266","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/operator/service/test/vectors/vote9.proof: -------------------------------------------------------------------------------- 1 | {"root":"14182664246125735476876251776179627595887296044725078266633887660202449145335","nullifier_hash":"3378201140505663456463497851027345453347054117297657165553493235060131236733","proof":{"pi_a":["3343177505096466619012210025649783266364737328097276561293111544837728369392","15141483867641509816238136126045122264561290629009397085724560411426145542427","1"],"pi_b":[["21108500009005580659887827522004374491230358691338776937645677785454706284742","12731313672060651468520261726872508641908649040384798232047421835361374436185"],["4904736304693071514751254063286749307021053156527743712027304240528529523565","9566192223499032715708199439575376122364453515347693368475063521341822173185"],["1","0"]],"pi_c":["5357950905115424912369489396203444449095398656120156672786706592702871007850","57378165006007638823076090310309564242074029439842547020285031798858372608","1"],"protocol":"groth"},"public_signal":["14182664246125735476876251776179627595887296044725078266633887660202449145335","3378201140505663456463497851027345453347054117297657165553493235060131236733","43379584054787486383572605962602545002668015983485933488536749112829893476306","9695771177025341492834515246141576816221841749730679787621778614635855226700"]} -------------------------------------------------------------------------------- /zkvote/snark/verifier.go: -------------------------------------------------------------------------------- 1 | package snark 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | goSnarkVerifier "github.com/arnaucube/go-snark/externalVerif" 7 | "github.com/arnaucube/go-snark/groth16" 8 | goSnarkUtils "github.com/arnaucube/go-snark/utils" 9 | 10 | "github.com/unitychain/zkvote-node/zkvote/common/utils" 11 | ) 12 | 13 | // VerifyByFile : verify proof 14 | // func VerifyByFile(vkPath string, pfPath string) bool { 15 | 16 | // dat, err := ioutil.ReadFile(pfPath) 17 | // ballot, err := ba.NewBallot(string(dat)) 18 | // if err != nil { 19 | // return false 20 | // } 21 | 22 | // vkFile, err := ioutil.ReadFile(vkPath) 23 | // if err != nil { 24 | // return false 25 | // } 26 | // return Verify(string(vkFile), ballot.Proof, ballot.PublicSignal) 27 | // } 28 | 29 | // Verify : verify proof 30 | func Verify(vkString string, proof *goSnarkVerifier.CircomProof, publicSignal []string) bool { 31 | 32 | // 33 | // verification key 34 | // 35 | var circomVk goSnarkVerifier.CircomVk 36 | err := json.Unmarshal([]byte(vkString), &circomVk) 37 | if err != nil { 38 | return false 39 | } 40 | 41 | var strVk goSnarkUtils.GrothVkString 42 | strVk.IC = circomVk.IC 43 | strVk.G1.Alpha = circomVk.Alpha1 44 | strVk.G2.Beta = circomVk.Beta2 45 | strVk.G2.Gamma = circomVk.Gamma2 46 | strVk.G2.Delta = circomVk.Delta2 47 | vk, err := goSnarkUtils.GrothVkFromString(strVk) 48 | if err != nil { 49 | utils.LogErrorf("GrothVkFromString error: %s", err.Error()) 50 | return false 51 | } 52 | // utils.LogInfof("vk parsed: %v", vk) 53 | 54 | // 55 | // proof 56 | // 57 | strProof := goSnarkUtils.GrothProofString{ 58 | PiA: proof.PiA, 59 | PiB: proof.PiB, 60 | PiC: proof.PiC, 61 | } 62 | grothProof, err := goSnarkUtils.GrothProofFromString(strProof) 63 | if err != nil { 64 | utils.LogErrorf("GrothProofFromString error: %s\n", err.Error()) 65 | return false 66 | } 67 | // fmt.Println("proof parsed:", grothProof) 68 | 69 | // 70 | // public signals 71 | // 72 | publicSignals, err := goSnarkUtils.ArrayStringToBigInt(publicSignal) 73 | if err != nil { 74 | utils.LogErrorf("ArrayStringToBigInt error: %s\n", err.Error()) 75 | return false 76 | } 77 | utils.LogDebugf("publicSignals parsed: %v", publicSignals) 78 | 79 | verified := groth16.VerifyProof(vk, grothProof, publicSignals, true) 80 | return verified 81 | } 82 | -------------------------------------------------------------------------------- /zkvote/snark/verifier_test.go: -------------------------------------------------------------------------------- 1 | package snark 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | . "github.com/unitychain/zkvote-node/zkvote/model/ballot" 8 | ) 9 | 10 | const proof = `{"root":"8689527539353720499147190441125863458163151721613317120863488267899702105029","nullifier_hash":"2816172240193667514175132752992643557697925698066061870859177551815944593050","proof":{"pi_a":["8691763105990886350963363271989552223474436289023125494979621270764800459232","7959638426364130174789515688875030651662144363587569706716497148362191159938","1"],"pi_b":[["19672397907949105136004182724814129941981129347700701231981910882913270766720","7453112622360304714622374664770379760730765585344569137151987786509397694750"],["17713299595964412202149265033076504022773960690349222314724526374801920909426","9367986521030447807907551596620994546862391690182193686900834575186052682863"],["1","0"]],"pi_c":["10492872918931104924574489517332635617291002221774517162909731671120506042533","18385559078235175716438291260368371366388578425177984394233419600921259384571","1"],"protocol":"groth"},"public_signal":["8689527539353720499147190441125863458163151721613317120863488267899702105029","2816172240193667514175132752992643557697925698066061870859177551815944593050","43379584054787486383572605962602545002668015983485933488536749112829893476306","9695771177025341492834515246141576816221841749730679787621778614635855226700"]}` 11 | const vk = `{ 12 | "protocol": "groth", 13 | "nPublic": 4, 14 | "IC": [ 15 | [ 16 | "12418545593032500588698912709858281690104918105591274283537109113822330018376", 17 | "20931904325052726909555873356162419291111683474434199586234246405351358007076", 18 | "1" 19 | ], 20 | [ 21 | "20628397838886466824304774917282912326347021119195946673626155488764554827426", 22 | "18932651013819139346274151857743429939893289761710026454765280889796912696842", 23 | "1" 24 | ], 25 | [ 26 | "9430082519347770932300688456187927018669734780076138387824045158783443793264", 27 | "19035983126452889389177117681190772422040879526183631526295743625992416117803", 28 | "1" 29 | ], 30 | [ 31 | "5339279297631183081611477158443448611463339794273824317466285743222921054272", 32 | "21057965667085293282174454954502607167023462692201738053860693743225240761449", 33 | "1" 34 | ], 35 | [ 36 | "11475864085691081945964608346166222023070665352547086256716493162333077451565", 37 | "15302677909953538721119124466918030428018783463721966486770201550923965579503", 38 | "1" 39 | ] 40 | ], 41 | "vk_alfa_1": [ 42 | "11417303501339734522061829883131690884464687546132933239999994469274434090237", 43 | "20801625657706405044121202841268227255984709948858400596558408013277081990093", 44 | "1" 45 | ], 46 | "vk_beta_2": [ 47 | [ 48 | "15257490107441725059621677412864036480078865260063490946726373442889551238440", 49 | "9020580066257313919895951867317841042871677370976861797204795731633729171698" 50 | ], 51 | [ 52 | "21334844345637983632423046114150286621525704308071587861080421838729635646372", 53 | "2765545994978726708626986051583881165994278094222716471116706295294794922202" 54 | ], 55 | [ 56 | "1", 57 | "0" 58 | ] 59 | ], 60 | "vk_gamma_2": [ 61 | [ 62 | "19299205038058634043829352835539955376769981433970440115917884366814859006292", 63 | "8342416061208732362064350898041247602669567628279882768725386578305247719325" 64 | ], 65 | [ 66 | "3442260395583412909421013007569773427454826955388410891893726285013570287982", 67 | "8361267789454297508215707315326381019461303200294243196889158571277886539726" 68 | ], 69 | [ 70 | "1", 71 | "0" 72 | ] 73 | ], 74 | "vk_delta_2": [ 75 | [ 76 | "5062182384391342192337991328239217874430714296685330421031162504423846795081", 77 | "15850203067089548369752564423001943762450669412562262968241788942611647064783" 78 | ], 79 | [ 80 | "20839684184298635835821096117900598379047635241034635840926363422579779728129", 81 | "11438468695066608516884166617488045468172155993365350530015168114983584945886" 82 | ], 83 | [ 84 | "1", 85 | "0" 86 | ] 87 | ], 88 | "vk_alfabeta_12": [ 89 | [ 90 | [ 91 | "1771370623011861096283290486633417905479293005029315111081408028652703459029", 92 | "12914249198801283864300209970317578031883024384357293584908854148710526229065" 93 | ], 94 | [ 95 | "8958020601366913821397254479696202425755572472670047355017483735522103210457", 96 | "20751269435344791505547470314310668628545745173873280868656671941079743778014" 97 | ], 98 | [ 99 | "15702126836192116074530195648947531023153582603146758933420631213344387899819", 100 | "1662909296365194722479908744193152065915598802598776435900971669257072537718" 101 | ] 102 | ], 103 | [ 104 | [ 105 | "188008691152688957740767049704383346935187291417385932698251564691849341039", 106 | "12816162447555779195804823867928366175328084184030453349996517432007728863345" 107 | ], 108 | [ 109 | "11237632839848656569396308683432285994371584434311292682559261560863943019074", 110 | "16382436169025775978479796332218873275691062872334788512550110225069525255474" 111 | ], 112 | [ 113 | "3622773912027958778557570061634500555588971011803724052062044732977297513461", 114 | "2716869227587311165419358441136455979986628100666908995776034320784110241968" 115 | ] 116 | ] 117 | ] 118 | } 119 | ` 120 | 121 | func TestParse(t *testing.T) { 122 | b, _ := NewBallot(proof) 123 | assert.Equal(t, "8689527539353720499147190441125863458163151721613317120863488267899702105029", b.Root) 124 | assert.Equal(t, "2816172240193667514175132752992643557697925698066061870859177551815944593050", b.NullifierHash) 125 | assert.Equal(t, 4, len(b.PublicSignal)) 126 | assert.Equal(t, "8689527539353720499147190441125863458163151721613317120863488267899702105029", b.PublicSignal[0]) 127 | assert.Equal(t, "8691763105990886350963363271989552223474436289023125494979621270764800459232", b.Proof.PiA[0]) 128 | } 129 | 130 | func TestVerify(t *testing.T) { 131 | b, _ := NewBallot(proof) 132 | assert.True(t, Verify(vk, b.Proof, b.PublicSignal)) 133 | } 134 | --------------------------------------------------------------------------------