From cfca6b41112fb07b431c96e3a6ee1418411d6c2a Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 1 Mar 2024 11:00:36 +0100 Subject: [PATCH 01/12] Introduces a start method to prover --- codex/codex.nim | 12 ++++++++++-- codex/node.nim | 2 +- codex/slots/proofs/prover.nim | 9 +++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/codex/codex.nim b/codex/codex.nim index e1ca98fa..37f11f7e 100644 --- a/codex/codex.nim +++ b/codex/codex.nim @@ -23,6 +23,7 @@ import pkg/stew/shims/net as stewnet import pkg/datastore import pkg/ethers except Rng import pkg/stew/io2 +import pkg/questionable import ./node import ./conf @@ -66,7 +67,7 @@ proc waitForSync(provider: Provider): Future[void] {.async.} = inc sleepTime proc bootstrapInteractions( - s: CodexServer): Future[void] {.async.} = + s: CodexServer): Future[?string] {.async.} = ## bootstrap interactions and return contracts ## using clients, hosts, validators pairings ## @@ -139,6 +140,7 @@ proc bootstrapInteractions( validator = some ValidatorInteractions.new(clock, validation) s.codexNode.contracts = (client, host, validator) + return await market.getZkeyHash() proc start*(s: CodexServer) {.async.} = trace "Starting codex node", config = $s.config @@ -173,7 +175,11 @@ proc start*(s: CodexServer) {.async.} = s.codexNode.discovery.updateAnnounceRecord(announceAddrs) s.codexNode.discovery.updateDhtRecord(s.config.nat, s.config.discoveryPort) - await s.bootstrapInteractions() + let proofCeremonyUrl = await s.bootstrapInteractions() + + if prover =? s.codexNode.prover: + prover.start(s.config, proofCeremonyUrl) + await s.codexNode.start() s.restServer.start() @@ -283,11 +289,13 @@ proc new*( $config.circomZkey else: "" + echo "Prover is some!" some Prover.new( store, CircomCompat.init($config.circomR1cs, $config.circomWasm, zkey), config.numProofSamples) else: + echo "Prover is none!" none Prover codexNode = CodexNodeRef.new( diff --git a/codex/node.nim b/codex/node.nim index b8466943..9ec19f99 100644 --- a/codex/node.nim +++ b/codex/node.nim @@ -64,7 +64,7 @@ type networkId: PeerId networkStore: NetworkStore engine: BlockExcEngine - prover: ?Prover + prover*: ?Prover discovery: Discovery contracts*: Contracts clock*: Clock diff --git a/codex/slots/proofs/prover.nim b/codex/slots/proofs/prover.nim index 9077c478..34fef2c5 100644 --- a/codex/slots/proofs/prover.nim +++ b/codex/slots/proofs/prover.nim @@ -21,6 +21,7 @@ import ../../merkletree import ../../stores import ../../market import ../../utils/poseidon2digest +import ../../conf import ../builder import ../sampler @@ -89,6 +90,14 @@ proc verify*( self.backend.verify(proof, inputs) +proc start*( + self: Prover, + config: CodexConf, + proofCeremonyUrl: ?string +) = + echo "prover start!" + echo proofCeremonyUrl + proc new*( _: type Prover, store: BlockStore, From 828484a646b8351a56912e7a8d682b6f7dd07bc8 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 1 Mar 2024 11:12:39 +0100 Subject: [PATCH 02/12] Moves backend creation into start method --- codex/codex.nim | 1 - codex/slots/proofs/prover.nim | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/codex/codex.nim b/codex/codex.nim index 37f11f7e..e794d5a5 100644 --- a/codex/codex.nim +++ b/codex/codex.nim @@ -292,7 +292,6 @@ proc new*( echo "Prover is some!" some Prover.new( store, - CircomCompat.init($config.circomR1cs, $config.circomWasm, zkey), config.numProofSamples) else: echo "Prover is none!" diff --git a/codex/slots/proofs/prover.nim b/codex/slots/proofs/prover.nim index 34fef2c5..047bae86 100644 --- a/codex/slots/proofs/prover.nim +++ b/codex/slots/proofs/prover.nim @@ -13,6 +13,7 @@ import pkg/chronicles import pkg/circomcompat import pkg/poseidon2 import pkg/questionable/results +import pkg/confutils/defs import pkg/libp2p/cid @@ -97,14 +98,13 @@ proc start*( ) = echo "prover start!" echo proofCeremonyUrl + self.backend = CircomCompat.init($config.circomR1cs, $config.circomWasm, $config.circomZkey) proc new*( _: type Prover, store: BlockStore, - backend: AnyBackend, nSamples: int): Prover = Prover( - backend: backend, store: store, nSamples: nSamples) From 63ab4c5064530195b0073bff3f5e64950e785488 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 1 Mar 2024 13:42:53 +0100 Subject: [PATCH 03/12] sets up three paths for backend initialization --- codex/codex.nim | 6 +-- codex/slots/proofs/prover.nim | 79 +++++++++++++++++++++++++---------- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/codex/codex.nim b/codex/codex.nim index e794d5a5..f0b588cd 100644 --- a/codex/codex.nim +++ b/codex/codex.nim @@ -178,7 +178,9 @@ proc start*(s: CodexServer) {.async.} = let proofCeremonyUrl = await s.bootstrapInteractions() if prover =? s.codexNode.prover: - prover.start(s.config, proofCeremonyUrl) + if err =? (await prover.start(s.config, proofCeremonyUrl)).errorOption: + error "Failed to start prover", msg = err.msg + return # should we abort start-up this way? await s.codexNode.start() s.restServer.start() @@ -289,12 +291,10 @@ proc new*( $config.circomZkey else: "" - echo "Prover is some!" some Prover.new( store, config.numProofSamples) else: - echo "Prover is none!" none Prover codexNode = CodexNodeRef.new( diff --git a/codex/slots/proofs/prover.nim b/codex/slots/proofs/prover.nim index 047bae86..ed17e258 100644 --- a/codex/slots/proofs/prover.nim +++ b/codex/slots/proofs/prover.nim @@ -44,7 +44,7 @@ type AnyProofInputs* = ProofInputs[Poseidon2Hash] Prover* = ref object of RootObj - backend: AnyBackend + backend: ?AnyBackend store: BlockStore nSamples: int @@ -63,24 +63,27 @@ proc prove*( trace "Received proof challenge" - without builder =? AnyBuilder.new(self.store, manifest), err: - error "Unable to create slots builder", err = err.msg - return failure(err) + if backend =? self.backend: + without builder =? AnyBuilder.new(self.store, manifest), err: + error "Unable to create slots builder", err = err.msg + return failure(err) - without sampler =? AnySampler.new(slotIdx, self.store, builder), err: - error "Unable to create data sampler", err = err.msg - return failure(err) + without sampler =? AnySampler.new(slotIdx, self.store, builder), err: + error "Unable to create data sampler", err = err.msg + return failure(err) - without proofInput =? await sampler.getProofInput(challenge, self.nSamples), err: - error "Unable to get proof input for slot", err = err.msg - return failure(err) + without proofInput =? await sampler.getProofInput(challenge, self.nSamples), err: + error "Unable to get proof input for slot", err = err.msg + return failure(err) - # prove slot - without proof =? self.backend.prove(proofInput), err: - error "Unable to prove slot", err = err.msg - return failure(err) + # prove slot + without proof =? backend.prove(proofInput), err: + error "Unable to prove slot", err = err.msg + return failure(err) - success (proofInput, proof) + success (proofInput, proof) + else: + return failure("Prover was not started") proc verify*( self: Prover, @@ -89,16 +92,49 @@ proc verify*( ## Prove a statement using backend. ## Returns a future that resolves to a proof. - self.backend.verify(proof, inputs) + if backend =? self.backend: + return backend.verify(proof, inputs) + else: + return failure("Prover was not started") + +proc initializeFromConfig( + self: Prover, + config: CodexConf): ?!void = + + # check provided files exist + # initialize backend with files + # or failure + + self.backend = some CircomCompat.init($config.circomR1cs, $config.circomWasm, $config.circomZkey) + success() + +proc initializeFromCeremonyFiles( + self: Prover): ?!void = + + # initialize from previously-downloaded files if they exist + echo "todo" + success() + +proc initializeFromCeremonyUrl( + self: Prover, + proofCeremonyUrl: ?string): Future[?!void] {.async.} = + + # download the ceremony url + # unzip it + return self.initializeFromCeremonyFiles() proc start*( self: Prover, config: CodexConf, - proofCeremonyUrl: ?string -) = - echo "prover start!" - echo proofCeremonyUrl - self.backend = CircomCompat.init($config.circomR1cs, $config.circomWasm, $config.circomZkey) + proofCeremonyUrl: ?string): Future[?!void] {.async.} = + if cliErr =? self.initializeFromConfig(config).errorOption: + info "Could not initialize prover backend from CLI options...", msg = cliErr.msg + if localErr =? self.initializeFromCeremonyFiles().errorOption: + info "Could not initialize prover backend from local files...", msg = localErr.msg + if urlErr =? (await self.initializeFromCeremonyUrl(proofCeremonyUrl)).errorOption: + warn "Could not initialize prover backend from ceremony url...", msg = urlErr.msg + return failure(urlErr) + return success() proc new*( _: type Prover, @@ -107,4 +143,5 @@ proc new*( Prover( store: store, + backend: none AnyBackend, nSamples: nSamples) From 83e134703807aeed9e8ac71c9debe09a71676156 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 1 Mar 2024 14:02:57 +0100 Subject: [PATCH 04/12] Extracts backend initialization to backend-factory --- codex/slots/proofs/backendfactory.nim | 44 +++++++++++++++++++++++++++ codex/slots/proofs/backends.nim | 3 ++ codex/slots/proofs/prover.nim | 42 +++++-------------------- 3 files changed, 54 insertions(+), 35 deletions(-) create mode 100644 codex/slots/proofs/backendfactory.nim diff --git a/codex/slots/proofs/backendfactory.nim b/codex/slots/proofs/backendfactory.nim new file mode 100644 index 00000000..f08d7867 --- /dev/null +++ b/codex/slots/proofs/backendfactory.nim @@ -0,0 +1,44 @@ +import pkg/chronos +import pkg/questionable +import pkg/confutils/defs + +import ../../conf +import ./backends + +proc initializeFromConfig( + config: CodexConf): ?!AnyBackend = + + # check provided files exist + # initialize backend with files + # or failure + + success(CircomCompat.init($config.circomR1cs, $config.circomWasm, $config.circomZkey)) + +proc initializeFromCeremonyFiles(): ?!AnyBackend = + + # initialize from previously-downloaded files if they exist + echo "todo" + failure("todo") + +proc initializeFromCeremonyUrl( + proofCeremonyUrl: ?string): Future[?!AnyBackend] {.async.} = + + # download the ceremony url + # unzip it + + without backend =? initializeFromCeremonyFiles(), err: + return failure(err) + return success(backend) + +proc initializeBackend*( + config: CodexConf, + proofCeremonyUrl: ?string): Future[?!AnyBackend] {.async.} = + + without backend =? initializeFromConfig(config), cliErr: + info "Could not initialize prover backend from CLI options...", msg = cliErr.msg + without backend =? initializeFromCeremonyFiles(), localErr: + info "Could not initialize prover backend from local files...", msg = localErr.msg + without backend =? (await initializeFromCeremonyUrl(proofCeremonyUrl)), urlErr: + warn "Could not initialize prover backend from ceremony url...", msg = urlErr.msg + return failure(urlErr) + return success(backend) diff --git a/codex/slots/proofs/backends.nim b/codex/slots/proofs/backends.nim index 477ba140..3872d821 100644 --- a/codex/slots/proofs/backends.nim +++ b/codex/slots/proofs/backends.nim @@ -1,3 +1,6 @@ import ./backends/circomcompat export circomcompat + +type + AnyBackend* = CircomCompat diff --git a/codex/slots/proofs/prover.nim b/codex/slots/proofs/prover.nim index ed17e258..40a39e73 100644 --- a/codex/slots/proofs/prover.nim +++ b/codex/slots/proofs/prover.nim @@ -13,7 +13,6 @@ import pkg/chronicles import pkg/circomcompat import pkg/poseidon2 import pkg/questionable/results -import pkg/confutils/defs import pkg/libp2p/cid @@ -28,6 +27,7 @@ import ../builder import ../sampler import ./backends +import ./backendfactory import ../types export backends @@ -36,7 +36,6 @@ logScope: topics = "codex prover" type - AnyBackend* = CircomCompat AnyProof* = CircomProof AnySampler* = Poseidon2Sampler @@ -97,43 +96,16 @@ proc verify*( else: return failure("Prover was not started") -proc initializeFromConfig( - self: Prover, - config: CodexConf): ?!void = - - # check provided files exist - # initialize backend with files - # or failure - - self.backend = some CircomCompat.init($config.circomR1cs, $config.circomWasm, $config.circomZkey) - success() - -proc initializeFromCeremonyFiles( - self: Prover): ?!void = - - # initialize from previously-downloaded files if they exist - echo "todo" - success() - -proc initializeFromCeremonyUrl( - self: Prover, - proofCeremonyUrl: ?string): Future[?!void] {.async.} = - - # download the ceremony url - # unzip it - return self.initializeFromCeremonyFiles() - proc start*( self: Prover, config: CodexConf, proofCeremonyUrl: ?string): Future[?!void] {.async.} = - if cliErr =? self.initializeFromConfig(config).errorOption: - info "Could not initialize prover backend from CLI options...", msg = cliErr.msg - if localErr =? self.initializeFromCeremonyFiles().errorOption: - info "Could not initialize prover backend from local files...", msg = localErr.msg - if urlErr =? (await self.initializeFromCeremonyUrl(proofCeremonyUrl)).errorOption: - warn "Could not initialize prover backend from ceremony url...", msg = urlErr.msg - return failure(urlErr) + + without backend =? (await initializeBackend(config, proofCeremonyUrl)), err: + error "Failed to initialize backend", msg = err.msg + return failure(err) + + self.backend = some backend return success() proc new*( From c67c2e7cd737e3f9e56f04f176d50c5e257d69a5 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 1 Mar 2024 14:25:48 +0100 Subject: [PATCH 05/12] Implements loading backend from cli files or previously downloaded local files --- codex/codex.nim | 22 ----------- codex/slots/proofs/backendfactory.nim | 56 +++++++++++++++++++++------ 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/codex/codex.nim b/codex/codex.nim index f0b588cd..eb2b26e4 100644 --- a/codex/codex.nim +++ b/codex/codex.nim @@ -269,28 +269,6 @@ proc new*( engine = BlockExcEngine.new(repoStore, wallet, network, blockDiscovery, peerStore, pendingBlocks) store = NetworkStore.new(engine, repoStore) prover = if config.prover: - if not fileAccessible($config.circomR1cs, {AccessFlags.Read}) and - endsWith($config.circomR1cs, ".r1cs"): - error "Circom R1CS file not accessible" - raise (ref Defect)( - msg: "r1cs file not readable, doesn't exist or wrong extension (.r1cs)") - - if not fileAccessible($config.circomWasm, {AccessFlags.Read}) and - endsWith($config.circomWasm, ".wasm"): - error "Circom wasm file not accessible" - raise (ref Defect)( - msg: "wasm file not readable, doesn't exist or wrong extension (.wasm)") - - let zkey = if not config.circomNoZkey: - if not fileAccessible($config.circomZkey, {AccessFlags.Read}) and - endsWith($config.circomZkey, ".zkey"): - error "Circom zkey file not accessible" - raise (ref Defect)( - msg: "zkey file not readable, doesn't exist or wrong extension (.zkey)") - - $config.circomZkey - else: "" - some Prover.new( store, config.numProofSamples) diff --git a/codex/slots/proofs/backendfactory.nim b/codex/slots/proofs/backendfactory.nim index f08d7867..dd048e85 100644 --- a/codex/slots/proofs/backendfactory.nim +++ b/codex/slots/proofs/backendfactory.nim @@ -1,32 +1,66 @@ +import os import pkg/chronos import pkg/questionable import pkg/confutils/defs +import pkg/stew/io2 import ../../conf import ./backends +proc initializeCircomBackend( + r1csFile: string, + wasmFile: string, + zKeyFile: string +): AnyBackend = + CircomCompat.init(r1csFile, wasmFile, zKeyFile) + proc initializeFromConfig( config: CodexConf): ?!AnyBackend = + if not fileAccessible($config.circomR1cs, {AccessFlags.Read}) and + endsWith($config.circomR1cs, ".r1cs"): + return failure("Circom R1CS file not accessible") - # check provided files exist - # initialize backend with files - # or failure + if not fileAccessible($config.circomWasm, {AccessFlags.Read}) and + endsWith($config.circomWasm, ".wasm"): + return failure("Circom wasm file not accessible") - success(CircomCompat.init($config.circomR1cs, $config.circomWasm, $config.circomZkey)) + if not fileAccessible($config.circomZkey, {AccessFlags.Read}) and + endsWith($config.circomZkey, ".zkey"): + return failure("Circom zkey file not accessible") -proc initializeFromCeremonyFiles(): ?!AnyBackend = + success(initializeCircomBackend( + $config.circomR1cs, + $config.circomWasm, + $config.circomZkey)) - # initialize from previously-downloaded files if they exist - echo "todo" - failure("todo") +proc r1csFilePath(config: CodexConf): string = + config.dataDir / "circuit.r1cs" + +proc wasmFilePath(config: CodexConf): string = + config.dataDir / "circuit.wasm" + +proc zkeyFilePath(config: CodexConf): string = + config.dataDir / "circuit.zkey" + +proc initializeFromCeremonyFiles(config: CodexConf): ?!AnyBackend = + if fileExists(config.r1csFilePath) and + fileExists(config.wasmFilePath) and + fileExists(config.zkeyFilePath): + return success(initializeCircomBackend( + config.r1csFilePath, + config.wasmFilePath, + config.zkeyFilePath)) + + failure("Ceremony files not found") proc initializeFromCeremonyUrl( + config: CodexConf, proofCeremonyUrl: ?string): Future[?!AnyBackend] {.async.} = # download the ceremony url # unzip it - without backend =? initializeFromCeremonyFiles(), err: + without backend =? initializeFromCeremonyFiles(config), err: return failure(err) return success(backend) @@ -36,9 +70,9 @@ proc initializeBackend*( without backend =? initializeFromConfig(config), cliErr: info "Could not initialize prover backend from CLI options...", msg = cliErr.msg - without backend =? initializeFromCeremonyFiles(), localErr: + without backend =? initializeFromCeremonyFiles(config), localErr: info "Could not initialize prover backend from local files...", msg = localErr.msg - without backend =? (await initializeFromCeremonyUrl(proofCeremonyUrl)), urlErr: + without backend =? (await initializeFromCeremonyUrl(config, proofCeremonyUrl)), urlErr: warn "Could not initialize prover backend from ceremony url...", msg = urlErr.msg return failure(urlErr) return success(backend) From 7934d415cde61407f40b542c25ed9a258776a33a Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 1 Mar 2024 14:59:24 +0100 Subject: [PATCH 06/12] Wires up downloading and unzipping --- .gitmodules | 3 +++ codex/slots/proofs/backendfactory.nim | 36 ++++++++++++++++++++++----- vendor/zip | 1 + 3 files changed, 34 insertions(+), 6 deletions(-) create mode 160000 vendor/zip diff --git a/.gitmodules b/.gitmodules index ad4e4792..b4a9c9f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -209,3 +209,6 @@ url = https://github.com/codex-storage/codex-storage-proofs-circuits.git ignore = untracked branch = master +[submodule "vendor/zip"] + path = vendor/zip + url = https://github.com/nim-lang/zip.git diff --git a/codex/slots/proofs/backendfactory.nim b/codex/slots/proofs/backendfactory.nim index dd048e85..e9f55093 100644 --- a/codex/slots/proofs/backendfactory.nim +++ b/codex/slots/proofs/backendfactory.nim @@ -1,4 +1,6 @@ import os +import httpclient +import zip/zipfiles import pkg/chronos import pkg/questionable import pkg/confutils/defs @@ -42,6 +44,9 @@ proc wasmFilePath(config: CodexConf): string = proc zkeyFilePath(config: CodexConf): string = config.dataDir / "circuit.zkey" +proc zipFilePath(config: CodexConf): string = + config.dataDir / "circuit.zip" + proc initializeFromCeremonyFiles(config: CodexConf): ?!AnyBackend = if fileExists(config.r1csFilePath) and fileExists(config.wasmFilePath) and @@ -53,16 +58,35 @@ proc initializeFromCeremonyFiles(config: CodexConf): ?!AnyBackend = failure("Ceremony files not found") +proc downloadCeremonyUrl( + config: CodexConf, + proofCeremonyUrl: string +) = + var client = newHttpClient() + client.downloadFile( + "https://circuit.codex.storage/proving-key/" & proofCeremonyUrl, config.zipFilePath) + +proc unzipCeremonyFile( + config: CodexConf): ?!void = + var z: ZipArchive + if not z.open(config.zipFilePath): + return failure("Unable to open zip file: " & config.zipFilePath) + z.extractAll($config.dataDir) + success() + proc initializeFromCeremonyUrl( config: CodexConf, proofCeremonyUrl: ?string): Future[?!AnyBackend] {.async.} = - # download the ceremony url - # unzip it - - without backend =? initializeFromCeremonyFiles(config), err: - return failure(err) - return success(backend) + if url =? proofCeremonyUrl: + downloadCeremonyUrl(config, url) + if err =? unzipCeremonyFile(config).errorOption: + return failure(err) + without backend =? initializeFromCeremonyFiles(config), err: + return failure(err) + return success(backend) + else: + return failure("Ceremony URL not found") proc initializeBackend*( config: CodexConf, diff --git a/vendor/zip b/vendor/zip new file mode 160000 index 00000000..06f5b0a0 --- /dev/null +++ b/vendor/zip @@ -0,0 +1 @@ +Subproject commit 06f5b0a0767b14c7595ed168611782be69e61543 From 983da3c24f7f932a7006950c51adef2e7dacf42b Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 4 Mar 2024 09:43:35 +0100 Subject: [PATCH 07/12] functional implementation --- codex/slots/proofs/backendfactory.nim | 52 +++++++++++++++++++-------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/codex/slots/proofs/backendfactory.nim b/codex/slots/proofs/backendfactory.nim index e9f55093..8c02f781 100644 --- a/codex/slots/proofs/backendfactory.nim +++ b/codex/slots/proofs/backendfactory.nim @@ -2,6 +2,7 @@ import os import httpclient import zip/zipfiles import pkg/chronos +import pkg/chronicles import pkg/questionable import pkg/confutils/defs import pkg/stew/io2 @@ -30,19 +31,20 @@ proc initializeFromConfig( endsWith($config.circomZkey, ".zkey"): return failure("Circom zkey file not accessible") + trace "Initialized prover backend from cli config" success(initializeCircomBackend( $config.circomR1cs, $config.circomWasm, $config.circomZkey)) proc r1csFilePath(config: CodexConf): string = - config.dataDir / "circuit.r1cs" + config.dataDir / "proof_main.r1cs" proc wasmFilePath(config: CodexConf): string = - config.dataDir / "circuit.wasm" + config.dataDir / "proof_main.wasm" proc zkeyFilePath(config: CodexConf): string = - config.dataDir / "circuit.zkey" + config.dataDir / "proof_main.zkey" proc zipFilePath(config: CodexConf): string = config.dataDir / "circuit.zip" @@ -51,6 +53,7 @@ proc initializeFromCeremonyFiles(config: CodexConf): ?!AnyBackend = if fileExists(config.r1csFilePath) and fileExists(config.wasmFilePath) and fileExists(config.zkeyFilePath): + trace "Initialized prover backend from local files" return success(initializeCircomBackend( config.r1csFilePath, config.wasmFilePath, @@ -58,28 +61,47 @@ proc initializeFromCeremonyFiles(config: CodexConf): ?!AnyBackend = failure("Ceremony files not found") -proc downloadCeremonyUrl( +proc downloadCeremony( config: CodexConf, - proofCeremonyUrl: string -) = - var client = newHttpClient() - client.downloadFile( - "https://circuit.codex.storage/proving-key/" & proofCeremonyUrl, config.zipFilePath) + ceremonyHash: string +): ?!void = + # TODO: + # In the future, the zip file will be stored in the Codex network + # instead of a url + ceremonyHash, we'll get a CID from the marketplace contract. + + echo "OVERRIDE!" + let hash = "1f512a1c6a089eff7eb22b810438c34fc59d4b0e7935dbc77ec77255d39ec094" + + let url = "https://circuit.codex.storage/proving-key/" & hash # was ceremonyHash + trace "Downloading ceremony file", url, filepath = config.zipFilePath + try: + # Nim's default webclient does not support SSL on all platforms. + # Not without shipping additional binaries and cert-files... :( + # So we're using curl for now. + var rc = execShellCmd("curl -o " & config.zipFilePath & " " & url) + if not rc == 0: + return failure("Download failed with return code: " & $rc) + except Exception as exc: + return failure(exc.msg) + trace "Download completed." + success() proc unzipCeremonyFile( config: CodexConf): ?!void = + trace "Unzipping..." var z: ZipArchive if not z.open(config.zipFilePath): return failure("Unable to open zip file: " & config.zipFilePath) z.extractAll($config.dataDir) success() -proc initializeFromCeremonyUrl( +proc initializeFromCeremonyHash( config: CodexConf, - proofCeremonyUrl: ?string): Future[?!AnyBackend] {.async.} = + ceremonyHash: ?string): Future[?!AnyBackend] {.async.} = - if url =? proofCeremonyUrl: - downloadCeremonyUrl(config, url) + if hash =? ceremonyHash: + if dlErr =? downloadCeremony(config, hash).errorOption: + return failure(dlErr) if err =? unzipCeremonyFile(config).errorOption: return failure(err) without backend =? initializeFromCeremonyFiles(config), err: @@ -90,13 +112,13 @@ proc initializeFromCeremonyUrl( proc initializeBackend*( config: CodexConf, - proofCeremonyUrl: ?string): Future[?!AnyBackend] {.async.} = + ceremonyHash: ?string): Future[?!AnyBackend] {.async.} = without backend =? initializeFromConfig(config), cliErr: info "Could not initialize prover backend from CLI options...", msg = cliErr.msg without backend =? initializeFromCeremonyFiles(config), localErr: info "Could not initialize prover backend from local files...", msg = localErr.msg - without backend =? (await initializeFromCeremonyUrl(config, proofCeremonyUrl)), urlErr: + without backend =? (await initializeFromCeremonyHash(config, ceremonyHash)), urlErr: warn "Could not initialize prover backend from ceremony url...", msg = urlErr.msg return failure(urlErr) return success(backend) From 8ff6b50c37199862122f3e2543e6617c6b6bbb8d Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 4 Mar 2024 13:09:35 +0100 Subject: [PATCH 08/12] Fixes testprover.nim --- codex/slots/proofs/backendfactory.nim | 5 +---- codex/slots/proofs/prover.nim | 4 ++-- tests/codex/slots/testprover.nim | 24 ++++++++++++++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/codex/slots/proofs/backendfactory.nim b/codex/slots/proofs/backendfactory.nim index 8c02f781..7f2e159d 100644 --- a/codex/slots/proofs/backendfactory.nim +++ b/codex/slots/proofs/backendfactory.nim @@ -69,10 +69,7 @@ proc downloadCeremony( # In the future, the zip file will be stored in the Codex network # instead of a url + ceremonyHash, we'll get a CID from the marketplace contract. - echo "OVERRIDE!" - let hash = "1f512a1c6a089eff7eb22b810438c34fc59d4b0e7935dbc77ec77255d39ec094" - - let url = "https://circuit.codex.storage/proving-key/" & hash # was ceremonyHash + let url = "https://circuit.codex.storage/proving-key/" & ceremonyHash trace "Downloading ceremony file", url, filepath = config.zipFilePath try: # Nim's default webclient does not support SSL on all platforms. diff --git a/codex/slots/proofs/prover.nim b/codex/slots/proofs/prover.nim index 40a39e73..48396de1 100644 --- a/codex/slots/proofs/prover.nim +++ b/codex/slots/proofs/prover.nim @@ -99,9 +99,9 @@ proc verify*( proc start*( self: Prover, config: CodexConf, - proofCeremonyUrl: ?string): Future[?!void] {.async.} = + ceremonyHash: ?string): Future[?!void] {.async.} = - without backend =? (await initializeBackend(config, proofCeremonyUrl)), err: + without backend =? (await initializeBackend(config, ceremonyHash)), err: error "Failed to initialize backend", msg = err.msg return failure(err) diff --git a/tests/codex/slots/testprover.nim b/tests/codex/slots/testprover.nim index 9deed96a..42cf2265 100644 --- a/tests/codex/slots/testprover.nim +++ b/tests/codex/slots/testprover.nim @@ -15,6 +15,8 @@ import pkg/codex/chunker import pkg/codex/blocktype as bt import pkg/codex/slots import pkg/codex/stores +import pkg/codex/conf +import pkg/confutils/defs import pkg/poseidon2/io import pkg/codex/utils/poseidon2digest @@ -57,13 +59,23 @@ suite "Test Prover": test "Should sample and prove a slot": let - r1cs = "tests/circuits/fixtures/proof_main.r1cs" - wasm = "tests/circuits/fixtures/proof_main.wasm" - - circomBackend = CircomCompat.init(r1cs, wasm) - prover = Prover.new(store, circomBackend, samples) + prover = Prover.new(store, samples) challenge = 1234567.toF.toBytes.toArray32 - (inputs, proof) = (await prover.prove(1, verifiable, challenge)).tryGet + config = CodexConf( + cmd: StartUpCmd.persistence, + nat: ValidIpAddress.init("127.0.0.1"), + discoveryIp: ValidIpAddress.init(IPv4_any()), + metricsAddress: ValidIpAddress.init("127.0.0.1"), + persistenceCmd: PersistenceCmd.prover, + circomR1cs: InputFile("tests/circuits/fixtures/proof_main.r1cs"), + circomWasm: InputFile("tests/circuits/fixtures/proof_main.wasm"), + circomZkey: InputFile("tests/circuits/fixtures/proof_main.zkey") + ) + ceremonyHash = string.none + + (await prover.start(config, ceremonyHash)).tryGet() + + let (inputs, proof) = (await prover.prove(1, verifiable, challenge)).tryGet check: (await prover.verify(proof, inputs)).tryGet == true From 3d008c71fcc89d1a30c260e289fe0c21b4437367 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 4 Mar 2024 15:04:13 +0100 Subject: [PATCH 09/12] Sets up tests for backendfactory --- codex/slots/proofs/backendfactory.nim | 75 +++++------- codex/slots/proofs/backendutils.nim | 45 +++++++ tests/codex/slots/testbackendfactory.nim | 144 +++++++++++++++++++++++ tests/codex/testslots.nim | 1 + 4 files changed, 220 insertions(+), 45 deletions(-) create mode 100644 codex/slots/proofs/backendutils.nim create mode 100644 tests/codex/slots/testbackendfactory.nim diff --git a/codex/slots/proofs/backendfactory.nim b/codex/slots/proofs/backendfactory.nim index 7f2e159d..0f6a9b31 100644 --- a/codex/slots/proofs/backendfactory.nim +++ b/codex/slots/proofs/backendfactory.nim @@ -1,6 +1,4 @@ import os -import httpclient -import zip/zipfiles import pkg/chronos import pkg/chronicles import pkg/questionable @@ -9,30 +7,25 @@ import pkg/stew/io2 import ../../conf import ./backends - -proc initializeCircomBackend( - r1csFile: string, - wasmFile: string, - zKeyFile: string -): AnyBackend = - CircomCompat.init(r1csFile, wasmFile, zKeyFile) +import ./backendutils proc initializeFromConfig( - config: CodexConf): ?!AnyBackend = - if not fileAccessible($config.circomR1cs, {AccessFlags.Read}) and - endsWith($config.circomR1cs, ".r1cs"): + config: CodexConf, + utils: BackendUtils): ?!AnyBackend = + if not fileAccessible($config.circomR1cs, {AccessFlags.Read}) or + not endsWith($config.circomR1cs, ".r1cs"): return failure("Circom R1CS file not accessible") - if not fileAccessible($config.circomWasm, {AccessFlags.Read}) and - endsWith($config.circomWasm, ".wasm"): + if not fileAccessible($config.circomWasm, {AccessFlags.Read}) or + not endsWith($config.circomWasm, ".wasm"): return failure("Circom wasm file not accessible") - if not fileAccessible($config.circomZkey, {AccessFlags.Read}) and - endsWith($config.circomZkey, ".zkey"): + if not fileAccessible($config.circomZkey, {AccessFlags.Read}) or + not endsWith($config.circomZkey, ".zkey"): return failure("Circom zkey file not accessible") trace "Initialized prover backend from cli config" - success(initializeCircomBackend( + success(utils.initializeCircomBackend( $config.circomR1cs, $config.circomWasm, $config.circomZkey)) @@ -49,12 +42,14 @@ proc zkeyFilePath(config: CodexConf): string = proc zipFilePath(config: CodexConf): string = config.dataDir / "circuit.zip" -proc initializeFromCeremonyFiles(config: CodexConf): ?!AnyBackend = +proc initializeFromCeremonyFiles( + config: CodexConf, + utils: BackendUtils): ?!AnyBackend = if fileExists(config.r1csFilePath) and fileExists(config.wasmFilePath) and fileExists(config.zkeyFilePath): trace "Initialized prover backend from local files" - return success(initializeCircomBackend( + return success(utils.initializeCircomBackend( config.r1csFilePath, config.wasmFilePath, config.zkeyFilePath)) @@ -63,7 +58,8 @@ proc initializeFromCeremonyFiles(config: CodexConf): ?!AnyBackend = proc downloadCeremony( config: CodexConf, - ceremonyHash: string + ceremonyHash: string, + utils: BackendUtils ): ?!void = # TODO: # In the future, the zip file will be stored in the Codex network @@ -71,37 +67,25 @@ proc downloadCeremony( let url = "https://circuit.codex.storage/proving-key/" & ceremonyHash trace "Downloading ceremony file", url, filepath = config.zipFilePath - try: - # Nim's default webclient does not support SSL on all platforms. - # Not without shipping additional binaries and cert-files... :( - # So we're using curl for now. - var rc = execShellCmd("curl -o " & config.zipFilePath & " " & url) - if not rc == 0: - return failure("Download failed with return code: " & $rc) - except Exception as exc: - return failure(exc.msg) - trace "Download completed." - success() + return utils.downloadFile(url, config.zipFilePath) proc unzipCeremonyFile( - config: CodexConf): ?!void = + config: CodexConf, + utils: BackendUtils): ?!void = trace "Unzipping..." - var z: ZipArchive - if not z.open(config.zipFilePath): - return failure("Unable to open zip file: " & config.zipFilePath) - z.extractAll($config.dataDir) - success() + return utils.unzipFile(config.zipFilePath, $config.dataDir) proc initializeFromCeremonyHash( config: CodexConf, - ceremonyHash: ?string): Future[?!AnyBackend] {.async.} = + ceremonyHash: ?string, + utils: BackendUtils): Future[?!AnyBackend] {.async.} = if hash =? ceremonyHash: - if dlErr =? downloadCeremony(config, hash).errorOption: + if dlErr =? downloadCeremony(config, hash, utils).errorOption: return failure(dlErr) - if err =? unzipCeremonyFile(config).errorOption: + if err =? unzipCeremonyFile(config, utils).errorOption: return failure(err) - without backend =? initializeFromCeremonyFiles(config), err: + without backend =? initializeFromCeremonyFiles(config, utils), err: return failure(err) return success(backend) else: @@ -109,13 +93,14 @@ proc initializeFromCeremonyHash( proc initializeBackend*( config: CodexConf, - ceremonyHash: ?string): Future[?!AnyBackend] {.async.} = + ceremonyHash: ?string, + utils: BackendUtils = BackendUtils()): Future[?!AnyBackend] {.async.} = - without backend =? initializeFromConfig(config), cliErr: + without backend =? initializeFromConfig(config, utils), cliErr: info "Could not initialize prover backend from CLI options...", msg = cliErr.msg - without backend =? initializeFromCeremonyFiles(config), localErr: + without backend =? initializeFromCeremonyFiles(config, utils), localErr: info "Could not initialize prover backend from local files...", msg = localErr.msg - without backend =? (await initializeFromCeremonyHash(config, ceremonyHash)), urlErr: + without backend =? (await initializeFromCeremonyHash(config, ceremonyHash, utils)), urlErr: warn "Could not initialize prover backend from ceremony url...", msg = urlErr.msg return failure(urlErr) return success(backend) diff --git a/codex/slots/proofs/backendutils.nim b/codex/slots/proofs/backendutils.nim new file mode 100644 index 00000000..9614e76a --- /dev/null +++ b/codex/slots/proofs/backendutils.nim @@ -0,0 +1,45 @@ +import os +import zip/zipfiles +import pkg/chronos +import pkg/chronicles +import pkg/questionable +import pkg/questionable/results + +import ./backends + +type + BackendUtils* = ref object of RootObj + +method initializeCircomBackend*( + self: BackendUtils, + r1csFile: string, + wasmFile: string, + zKeyFile: string +): AnyBackend {.base.} = + CircomCompat.init(r1csFile, wasmFile, zKeyFile) + +method downloadFile*( + self: BackendUtils, + url: string, + filepath: string +): ?!void {.base.} = + try: + # Nim's default webclient does not support SSL on all platforms. + # Not without shipping additional binaries and cert-files... :( + # So we're using curl for now. + var rc = execShellCmd("curl -o " & filepath & " " & url) + if not rc == 0: + return failure("Download of '" & url & "' failed with return code: " & $rc) + except Exception as exc: + return failure(exc.msg) + success() + +method unzipFile*( + self: BackendUtils, + zipFile: string, + outputDir: string): ?!void {.base.} = + var z: ZipArchive + if not z.open(zipFile): + return failure("Unable to open zip file: " & zipFile) + z.extractAll(outputDir) + success() diff --git a/tests/codex/slots/testbackendfactory.nim b/tests/codex/slots/testbackendfactory.nim new file mode 100644 index 00000000..05a32e17 --- /dev/null +++ b/tests/codex/slots/testbackendfactory.nim @@ -0,0 +1,144 @@ +import os +import std/strutils +import std/sugar +import std/math + +import ../../asynctest + +import pkg/chronos +import pkg/confutils/defs +import pkg/codex/conf +import pkg/codex/slots/proofs/backends +import pkg/codex/slots/proofs/backendfactory +import pkg/codex/slots/proofs/backendutils + +import ./helpers +import ../helpers + +type + BackendUtilsMock = ref object of BackendUtils + argR1csFile: string + argWasmFile: string + argZKeyFile: string + argUrl: string + argFilepath: string + argZipFile: string + argOutputDir: string + +method initializeCircomBackend*( + self: BackendUtilsMock, + r1csFile: string, + wasmFile: string, + zKeyFile: string +): AnyBackend = + self.argR1csFile = r1csFile + self.argWasmFile = wasmFile + self.argZKeyFile = zKeyFile + +method downloadFile*( + self: BackendUtilsMock, + url: string, + filepath: string +): ?!void = + self.argUrl = url + self.argFilepath = filepath + success() + +method unzipFile*( + self: BackendUtilsMock, + zipFile: string, + outputDir: string): ?!void = + self.argZipFile = zipFile + self.argOutputDir = outputDir + try: + writeFile(outputDir / "proof_main.r1cs", "r1cs_file") + writeFile(outputDir / "proof_main.wasm", "wasm_file") + writeFile(outputDir / "proof_main.zkey", "zkey_file") + except Exception as exc: + return failure(exc.msg) + success() + +suite "Test BackendFactory": + let + utilsMock = BackendUtilsMock() + datadir = "testdatadir" + + setup: + createDir(datadir) + + teardown: + removeDir(datadir) + + test "Should create backend from cli config": + let + config = CodexConf( + cmd: StartUpCmd.persistence, + nat: ValidIpAddress.init("127.0.0.1"), + discoveryIp: ValidIpAddress.init(IPv4_any()), + metricsAddress: ValidIpAddress.init("127.0.0.1"), + persistenceCmd: PersistenceCmd.prover, + circomR1cs: InputFile("tests/circuits/fixtures/proof_main.r1cs"), + circomWasm: InputFile("tests/circuits/fixtures/proof_main.wasm"), + circomZkey: InputFile("tests/circuits/fixtures/proof_main.zkey") + ) + ceremonyHash = string.none + backend = (await initializeBackend(config, ceremonyHash, utilsMock)).tryGet + + check: + utilsMock.argR1csFile == $config.circomR1cs + utilsMock.argWasmFile == $config.circomWasm + utilsMock.argZKeyFile == $config.circomZkey + isEmptyOrWhitespace(utilsMock.argUrl) + isEmptyOrWhitespace(utilsMock.argFilepath) + isEmptyOrWhitespace(utilsMock.argZipFile) + isEmptyOrWhitespace(utilsMock.argOutputDir) + + test "Should create backend from local files": + let + config = CodexConf( + cmd: StartUpCmd.persistence, + nat: ValidIpAddress.init("127.0.0.1"), + discoveryIp: ValidIpAddress.init(IPv4_any()), + metricsAddress: ValidIpAddress.init("127.0.0.1"), + persistenceCmd: PersistenceCmd.prover, + + # Set the datadir such that the tests/circuits/fixtures/ files + # will be picked up as local files: + dataDir: OutDir("tests/circuits/fixtures") + ) + ceremonyHash = string.none + backend = (await initializeBackend(config, ceremonyHash, utilsMock)).tryGet + + check: + utilsMock.argR1csFile == config.dataDir / "proof_main.r1cs" + utilsMock.argWasmFile == config.dataDir / "proof_main.wasm" + utilsMock.argZKeyFile == config.dataDir / "proof_main.zkey" + isEmptyOrWhitespace(utilsMock.argUrl) + isEmptyOrWhitespace(utilsMock.argFilepath) + isEmptyOrWhitespace(utilsMock.argZipFile) + isEmptyOrWhitespace(utilsMock.argOutputDir) + + test "Should download and unzip ceremony file if not available": + let + ceremonyHash = some "12345" + expectedZip = datadir / "circuit.zip" + expectedUrl = "https://circuit.codex.storage/proving-key/" & !ceremonyHash + config = CodexConf( + cmd: StartUpCmd.persistence, + nat: ValidIpAddress.init("127.0.0.1"), + discoveryIp: ValidIpAddress.init(IPv4_any()), + metricsAddress: ValidIpAddress.init("127.0.0.1"), + persistenceCmd: PersistenceCmd.prover, + dataDir: OutDir(datadir) + ) + + backend = (await initializeBackend(config, ceremonyHash, utilsMock)).tryGet + + check: + utilsMock.argR1csFile == config.dataDir / "proof_main.r1cs" + utilsMock.argWasmFile == config.dataDir / "proof_main.wasm" + utilsMock.argZKeyFile == config.dataDir / "proof_main.zkey" + utilsMock.argUrl == expectedUrl + utilsMock.argFilepath == expectedZip + utilsMock.argZipFile == expectedZip + utilsMock.argOutputDir == datadir diff --git a/tests/codex/testslots.nim b/tests/codex/testslots.nim index 2292c4f8..059de7c2 100644 --- a/tests/codex/testslots.nim +++ b/tests/codex/testslots.nim @@ -3,5 +3,6 @@ import ./slots/testsampler import ./slots/testconverters import ./slots/testbackends import ./slots/testprover +import ./slots/testbackendfactory {.warning[UnusedImport]: off.} From 97bc1f86765373326fec79dd4dabe682229e900f Mon Sep 17 00:00:00 2001 From: benbierens Date: Tue, 5 Mar 2024 09:03:51 +0100 Subject: [PATCH 10/12] includes libzip-dev --- BUILDING.md | 6 +++--- docker/codex.Dockerfile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index ad66646f..4d617c15 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -33,7 +33,7 @@ The current implementation of Codex's zero-knowledge proving circuit requires th On a bare bones installation of Debian (or a distribution derived from Debian, such as Ubuntu), run ```shell -apt-get update && apt-get install build-essential cmake curl git rustc cargo +$ apt-get update && apt-get install build-essential cmake curl git rustc cargo libzip-dev ``` Non-Debian distributions have different package managers: `apk`, `dnf`, `pacman`, `rpm`, `yum`, etc. @@ -41,7 +41,7 @@ Non-Debian distributions have different package managers: `apk`, `dnf`, `pacman` For example, on a bare bones installation of Fedora, run ```shell -dnf install @development-tools cmake gcc-c++ rust cargo +dnf install @development-tools cmake gcc-c++ libzip rust cargo ``` ### macOS @@ -53,7 +53,7 @@ xcode-select --install Install [Homebrew (`brew`)](https://brew.sh/) and in a new terminal run ```shell -brew install bash cmake rust +brew install bash cmake rust libzip ``` Check that `PATH` is setup correctly diff --git a/docker/codex.Dockerfile b/docker/codex.Dockerfile index 673b04a8..c9eda169 100644 --- a/docker/codex.Dockerfile +++ b/docker/codex.Dockerfile @@ -30,7 +30,7 @@ ARG NAT_IP_AUTO WORKDIR ${APP_HOME} COPY --from=builder ${BUILD_HOME}/build/codex /usr/local/bin COPY --chmod=0755 docker/docker-entrypoint.sh / -RUN apt-get update && apt-get install -y libgomp1 bash curl jq && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y libgomp1 bash curl jq libzip-dev && rm -rf /var/lib/apt/lists/* ENV NAT_IP_AUTO=${NAT_IP_AUTO} ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["codex"] From 09796a707580788d321b8e5067a3c046801855b7 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 8 Mar 2024 13:02:24 +0100 Subject: [PATCH 11/12] pulls in updated contracts --- vendor/codex-contracts-eth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/codex-contracts-eth b/vendor/codex-contracts-eth index 118ee0b2..c3d7db34 160000 --- a/vendor/codex-contracts-eth +++ b/vendor/codex-contracts-eth @@ -1 +1 @@ -Subproject commit 118ee0b22b2d12c8fbf3376cf201d203d0a7cf97 +Subproject commit c3d7db345649d8a2dafabcede90edf5ff6b0bfc7 From 50ab23d4e087a48a1edf02c83606349e679b76bb Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 8 Mar 2024 14:31:13 +0100 Subject: [PATCH 12/12] removes integration cli tests for r1cs, wasm, and zkey file arguments. --- tests/integration/testcli.nim | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/tests/integration/testcli.nim b/tests/integration/testcli.nim index ee0aabe0..a13d3db3 100644 --- a/tests/integration/testcli.nim +++ b/tests/integration/testcli.nim @@ -24,27 +24,3 @@ suite "Command line interface": node.waitUntilOutput("Ethereum private key file does not have safe file permissions") node.stop() discard removeFile(unsafeKeyFile) - - test "complains when persistence is enabled without accessible r1cs file": - let node = startNode(@["persistence", "prover"]) - node.waitUntilOutput("r1cs file not readable, doesn't exist or wrong extension (.r1cs)") - node.stop() - - test "complains when persistence is enabled without accessible wasm file": - let node = startNode(@[ - "persistence", - "prover", - "--circom-r1cs=tests/circuits/fixtures/proof_main.r1cs" - ]) - node.waitUntilOutput("wasm file not readable, doesn't exist or wrong extension (.wasm)") - node.stop() - - test "complains when persistence is enabled without accessible zkey file": - let node = startNode(@[ - "persistence", - "prover", - "--circom-r1cs=tests/circuits/fixtures/proof_main.r1cs", - "--circom-wasm=tests/circuits/fixtures/proof_main.wasm" - ]) - node.waitUntilOutput("zkey file not readable, doesn't exist or wrong extension (.zkey)") - node.stop()