Skip to content

Commit

Permalink
Smart contracts update: Groth16Proof instead of bytes (#683)
Browse files Browse the repository at this point in the history
* Smart contracts update: Groth16Proof instead of bytes

* Use dummy verifier for now, until we can create ZK proofs

* Fix tests: submit proof only when slot is filled

* Submit dummy proofs for now

* More detailed log when proof submission failed

* Use dummy verifier for integration tests

For now at least

* Fix mistake in blanket renaming to ethProvider

* Update to latest codex-contracts-eth

* feat: zkey-hash from chain

* Fix zkeyHash

---------

Co-authored-by: Adam Uhlíř <[email protected]>
  • Loading branch information
markspanbroek and AuHau authored Feb 7, 2024
1 parent 403b9ba commit 2cf892c
Show file tree
Hide file tree
Showing 26 changed files with 164 additions and 88 deletions.
4 changes: 3 additions & 1 deletion codex/contracts/config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ type
period*: UInt256 # proofs requirements are calculated per period (in seconds)
timeout*: UInt256 # mark proofs as missing before the timeout (in seconds)
downtime*: uint8 # ignore this much recent blocks for proof requirements
zkeyHash*: string # hash of the zkey file which is linked to the verifier


func fromTuple(_: type ProofConfig, tupl: tuple): ProofConfig =
ProofConfig(
period: tupl[0],
timeout: tupl[1],
downtime: tupl[2]
downtime: tupl[2],
zkeyHash: tupl[3]
)

func fromTuple(_: type CollateralConfig, tupl: tuple): CollateralConfig =
Expand Down
5 changes: 4 additions & 1 deletion codex/contracts/deployment.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ type Deployment* = ref object
const knownAddresses = {
# Hardhat localhost network
"31337": {
"Marketplace": Address.init("0x59b670e9fA9D0A427751Af201D676719a970857b")
# TODO: This is currently the address of the marketplace with a dummy
# verifier. Replace with "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44" once we
# can generate actual Groth16 ZK proofs
"Marketplace": Address.init("0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f"),
}.toTable,
# Taiko Alpha-3 Testnet
"167005": {
Expand Down
11 changes: 8 additions & 3 deletions codex/contracts/market.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import pkg/questionable
import ../logutils
import ../market
import ./marketplace
import ./proofs

export market

Expand Down Expand Up @@ -38,6 +39,10 @@ proc approveFunds(market: OnChainMarket, amount: UInt256) {.async.} =

discard await token.increaseAllowance(market.contract.address(), amount).confirm(1)

method getZkeyHash*(market: OnChainMarket): Future[?string] {.async.} =
let config = await market.contract.config()
return some config.proofs.zkeyHash

method getSigner*(market: OnChainMarket): Future[Address] {.async.} =
return await market.signer.getAddress()

Expand Down Expand Up @@ -120,7 +125,7 @@ method getActiveSlot*(market: OnChainMarket,
method fillSlot(market: OnChainMarket,
requestId: RequestId,
slotIndex: UInt256,
proof: seq[byte],
proof: Groth16Proof,
collateral: UInt256) {.async.} =
await market.approveFunds(collateral)
await market.contract.fillSlot(requestId, slotIndex, proof)
Expand Down Expand Up @@ -155,7 +160,7 @@ method getChallenge*(market: OnChainMarket, id: SlotId): Future[ProofChallenge]

method submitProof*(market: OnChainMarket,
id: SlotId,
proof: seq[byte]) {.async.} =
proof: Groth16Proof) {.async.} =
await market.contract.submitProof(id, proof)

method markProofAsMissing*(market: OnChainMarket,
Expand Down Expand Up @@ -272,7 +277,7 @@ method subscribeProofSubmission*(market: OnChainMarket,
callback: OnProofSubmitted):
Future[MarketSubscription] {.async.} =
proc onEvent(event: ProofSubmitted) {.upraises: [].} =
callback(event.id, event.proof)
callback(event.id)
let subscription = await market.contract.subscribe(ProofSubmitted, onEvent)
return OnChainMarketSubscription(eventSubscription: subscription)

Expand Down
6 changes: 3 additions & 3 deletions codex/contracts/marketplace.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import pkg/stint
import pkg/chronos
import ../clock
import ./requests
import ./proofs
import ./config

export stint
Expand Down Expand Up @@ -33,7 +34,6 @@ type
requestId* {.indexed.}: RequestId
ProofSubmitted* = object of Event
id*: SlotId
proof*: seq[byte]


proc config*(marketplace: Marketplace): MarketplaceConfig {.contract, view.}
Expand All @@ -43,7 +43,7 @@ proc slashPercentage*(marketplace: Marketplace): UInt256 {.contract, view.}
proc minCollateralThreshold*(marketplace: Marketplace): UInt256 {.contract, view.}

proc requestStorage*(marketplace: Marketplace, request: StorageRequest) {.contract.}
proc fillSlot*(marketplace: Marketplace, requestId: RequestId, slotIndex: UInt256, proof: seq[byte]) {.contract.}
proc fillSlot*(marketplace: Marketplace, requestId: RequestId, slotIndex: UInt256, proof: Groth16Proof) {.contract.}
proc withdrawFunds*(marketplace: Marketplace, requestId: RequestId) {.contract.}
proc freeSlot*(marketplace: Marketplace, id: SlotId) {.contract.}
proc getRequest*(marketplace: Marketplace, id: RequestId): StorageRequest {.contract, view.}
Expand All @@ -65,5 +65,5 @@ proc willProofBeRequired*(marketplace: Marketplace, id: SlotId): bool {.contract
proc getChallenge*(marketplace: Marketplace, id: SlotId): array[32, byte] {.contract, view.}
proc getPointer*(marketplace: Marketplace, id: SlotId): uint8 {.contract, view.}

proc submitProof*(marketplace: Marketplace, id: SlotId, proof: seq[byte]) {.contract.}
proc submitProof*(marketplace: Marketplace, id: SlotId, proof: Groth16Proof) {.contract.}
proc markProofAsMissing*(marketplace: Marketplace, id: SlotId, period: UInt256) {.contract.}
33 changes: 33 additions & 0 deletions codex/contracts/proofs.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pkg/stint
import pkg/contractabi
import pkg/ethers/fields

type
Groth16Proof* = object
a*: G1Point
b*: G2Point
c*: G1Point
G1Point* = object
x*: UInt256
y*: UInt256
G2Point* = object
x*: array[2, UInt256]
y*: array[2, UInt256]

func solidityType*(_: type G1Point): string =
solidityType(G1Point.fieldTypes)

func solidityType*(_: type G2Point): string =
solidityType(G2Point.fieldTypes)

func solidityType*(_: type Groth16Proof): string =
solidityType(Groth16Proof.fieldTypes)

func encode*(encoder: var AbiEncoder, point: G1Point) =
encoder.write(point.fieldValues)

func encode*(encoder: var AbiEncoder, point: G2Point) =
encoder.write(point.fieldValues)

func encode*(encoder: var AbiEncoder, proof: Groth16Proof) =
encoder.write(proof.fieldValues)
11 changes: 8 additions & 3 deletions codex/market.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import pkg/upraises
import pkg/questionable
import pkg/ethers/erc20
import ./contracts/requests
import ./contracts/proofs
import ./clock
import ./periods

export chronos
export questionable
export requests
export proofs
export SecondsSince1970
export periods

Expand All @@ -23,13 +25,16 @@ type
OnSlotFreed* = proc(requestId: RequestId, slotIndex: UInt256) {.gcsafe, upraises: [].}
OnRequestCancelled* = proc(requestId: RequestId) {.gcsafe, upraises:[].}
OnRequestFailed* = proc(requestId: RequestId) {.gcsafe, upraises:[].}
OnProofSubmitted* = proc(id: SlotId, proof: seq[byte]) {.gcsafe, upraises:[].}
OnProofSubmitted* = proc(id: SlotId) {.gcsafe, upraises:[].}
PastStorageRequest* = object
requestId*: RequestId
ask*: StorageAsk
expiry*: UInt256
ProofChallenge* = array[32, byte]

method getZkeyHash*(market: Market): Future[?string] {.base, async.} =
raiseAssert("not implemented")

method getSigner*(market: Market): Future[Address] {.base, async.} =
raiseAssert("not implemented")

Expand Down Expand Up @@ -91,7 +96,7 @@ method getActiveSlot*(
method fillSlot*(market: Market,
requestId: RequestId,
slotIndex: UInt256,
proof: seq[byte],
proof: Groth16Proof,
collateral: UInt256) {.base, async.} =
raiseAssert("not implemented")

Expand Down Expand Up @@ -120,7 +125,7 @@ method getChallenge*(market: Market, id: SlotId): Future[ProofChallenge] {.base,

method submitProof*(market: Market,
id: SlotId,
proof: seq[byte]) {.base, async.} =
proof: Groth16Proof) {.base, async.} =
raiseAssert("not implemented")

method markProofAsMissing*(market: Market,
Expand Down
11 changes: 8 additions & 3 deletions codex/node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ proc onStore(
proc onProve(
self: CodexNodeRef,
slot: Slot,
challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
## Generats a proof for a given slot and challenge
##

Expand Down Expand Up @@ -585,7 +585,12 @@ proc onProve(
return failure(err)

# Todo: send proofInput to circuit. Get proof. (Profit, repeat.)
success(@[42'u8])

# For now: dummy proof that is not all zero's, so that it is accepted by the
# dummy verifier:
var proof = Groth16Proof.default
proof.a.x = 42.u256
success(proof)

proc onExpiryUpdate(
self: CodexNodeRef,
Expand Down Expand Up @@ -635,7 +640,7 @@ proc start*(self: CodexNodeRef) {.async.} =
self.onClear(request, slotIndex)

hostContracts.sales.onProve =
proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] =
proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] =
# TODO: generate proof
self.onProve(slot, challenge)

Expand Down
2 changes: 1 addition & 1 deletion codex/sales/salescontext.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type
OnStore* = proc(request: StorageRequest,
slot: UInt256,
blocksCb: BlocksCb): Future[?!void] {.gcsafe, upraises: [].}
OnProve* = proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.gcsafe, upraises: [].}
OnProve* = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.gcsafe, upraises: [].}
OnExpiryUpdate* = proc(rootCid: string, expiry: SecondsSince1970): Future[?!void] {.gcsafe, upraises: [].}
OnClear* = proc(request: StorageRequest,
slotIndex: UInt256) {.gcsafe, upraises: [].}
Expand Down
2 changes: 1 addition & 1 deletion codex/sales/states/filling.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ logScope:

type
SaleFilling* = ref object of ErrorHandlingState
proof*: seq[byte]
proof*: Groth16Proof

method `$`*(state: SaleFilling): string = "SaleFilling"

Expand Down
3 changes: 2 additions & 1 deletion codex/sales/states/proving.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import std/options
import pkg/questionable/results
import ../../clock
import ../../logutils
import ../../utils/exceptions
import ../statemachine
import ../salesagent
import ../salescontext
Expand Down Expand Up @@ -35,7 +36,7 @@ method prove*(
debug "Submitting proof", currentPeriod = currentPeriod, slotId = slot.id
await market.submitProof(slot.id, proof)
except CatchableError as e:
error "Submitting proof failed", msg = e.msg
error "Submitting proof failed", msg = e.msgDetail

proc proveLoop(
state: SaleProving,
Expand Down
2 changes: 1 addition & 1 deletion codex/sales/states/provingsimulated.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ when codex_enable_proof_failures:

try:
warn "Submitting INVALID proof", period = currentPeriod, slotId = slot.id
await market.submitProof(slot.id, newSeq[byte](0))
await market.submitProof(slot.id, Groth16Proof.default)
except ProviderError as e:
if not e.revertReason.contains("Invalid proof"):
onSubmitProofError(e, currentPeriod, slot.id)
Expand Down
15 changes: 9 additions & 6 deletions tests/codex/helpers/mockmarket.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import std/sugar
import pkg/questionable
import pkg/codex/market
import pkg/codex/contracts/requests
import pkg/codex/contracts/proofs
import pkg/codex/contracts/config
import ../examples

Expand All @@ -24,6 +25,7 @@ type
fulfilled*: seq[Fulfillment]
filled*: seq[MockSlot]
freed*: seq[SlotId]
submitted*: seq[Groth16Proof]
markedAsMissingProofs*: seq[SlotId]
canBeMarkedAsMissing: HashSet[SlotId]
withdrawn*: seq[RequestId]
Expand All @@ -36,13 +38,13 @@ type
config*: MarketplaceConfig
Fulfillment* = object
requestId*: RequestId
proof*: seq[byte]
proof*: Groth16Proof
host*: Address
MockSlot* = object
requestId*: RequestId
host*: Address
slotIndex*: UInt256
proof*: seq[byte]
proof*: Groth16Proof
Subscriptions = object
onRequest: seq[RequestSubscription]
onFulfillment: seq[FulfillmentSubscription]
Expand Down Expand Up @@ -215,7 +217,7 @@ proc emitRequestFailed*(market: MockMarket, requestId: RequestId) =
proc fillSlot*(market: MockMarket,
requestId: RequestId,
slotIndex: UInt256,
proof: seq[byte],
proof: Groth16Proof,
host: Address) =
let slot = MockSlot(
requestId: requestId,
Expand All @@ -230,7 +232,7 @@ proc fillSlot*(market: MockMarket,
method fillSlot*(market: MockMarket,
requestId: RequestId,
slotIndex: UInt256,
proof: seq[byte],
proof: Groth16Proof,
collateral: UInt256) {.async.} =
market.fillSlot(requestId, slotIndex, proof, market.signer)

Expand Down Expand Up @@ -273,9 +275,10 @@ method getChallenge*(mock: MockMarket, id: SlotId): Future[ProofChallenge] {.asy
proc setProofEnd*(mock: MockMarket, id: SlotId, proofEnd: UInt256) =
mock.proofEnds[id] = proofEnd

method submitProof*(mock: MockMarket, id: SlotId, proof: seq[byte]) {.async.} =
method submitProof*(mock: MockMarket, id: SlotId, proof: Groth16Proof) {.async.} =
mock.submitted.add(proof)
for subscription in mock.subscriptions.onProofSubmitted:
subscription.callback(id, proof)
subscription.callback(id)

method markProofAsMissing*(market: MockMarket,
id: SlotId,
Expand Down
2 changes: 1 addition & 1 deletion tests/codex/sales/states/testfilled.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ checksuite "sales state 'filled'":
slot = MockSlot(requestId: request.id,
host: Address.example,
slotIndex: slotIndex,
proof: @[])
proof: Groth16Proof.default)

market.requestEnds[request.id] = 321
onExpiryUpdatePassedExpiry = -1
Expand Down
6 changes: 3 additions & 3 deletions tests/codex/sales/states/testinitialproving.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import ../../helpers
import ../../helpers/mockmarket

asyncchecksuite "sales state 'initialproving'":
let proof = exampleProof()
let proof = Groth16Proof.example
let request = StorageRequest.example
let slotIndex = (request.ask.slots div 2).u256
let market = MockMarket.new()
Expand All @@ -26,7 +26,7 @@ asyncchecksuite "sales state 'initialproving'":
var receivedChallenge: ProofChallenge

setup:
let onProve = proc (slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
let onProve = proc (slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
receivedChallenge = challenge
return success(proof)
let context = SalesContext(
Expand Down Expand Up @@ -60,7 +60,7 @@ asyncchecksuite "sales state 'initialproving'":
check receivedChallenge == market.proofChallenge

test "switches to errored state when onProve callback fails":
let onProveFailed: OnProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
let onProveFailed: OnProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
return failure("oh no!")

let proofFailedContext = SalesContext(
Expand Down
Loading

0 comments on commit 2cf892c

Please sign in to comment.