Smart contracts update: Groth16Proof instead of bytes (#683)
* 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íř <adam@uhlir.dev>
This commit is contained in:
parent
403b9baf9f
commit
2cf892c467
|
@ -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 =
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -8,6 +8,7 @@ import pkg/questionable
|
|||
import ../logutils
|
||||
import ../market
|
||||
import ./marketplace
|
||||
import ./proofs
|
||||
|
||||
export market
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import pkg/stint
|
|||
import pkg/chronos
|
||||
import ../clock
|
||||
import ./requests
|
||||
import ./proofs
|
||||
import ./config
|
||||
|
||||
export stint
|
||||
|
@ -33,7 +34,6 @@ type
|
|||
requestId* {.indexed.}: RequestId
|
||||
ProofSubmitted* = object of Event
|
||||
id*: SlotId
|
||||
proof*: seq[byte]
|
||||
|
||||
|
||||
proc config*(marketplace: Marketplace): MarketplaceConfig {.contract, view.}
|
||||
|
@ -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.}
|
||||
|
@ -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.}
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
##
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -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: [].}
|
||||
|
|
|
@ -12,7 +12,7 @@ logScope:
|
|||
|
||||
type
|
||||
SaleFilling* = ref object of ErrorHandlingState
|
||||
proof*: seq[byte]
|
||||
proof*: Groth16Proof
|
||||
|
||||
method `$`*(state: SaleFilling): string = "SaleFilling"
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import std/options
|
|||
import pkg/questionable/results
|
||||
import ../../clock
|
||||
import ../../logutils
|
||||
import ../../utils/exceptions
|
||||
import ../statemachine
|
||||
import ../salesagent
|
||||
import ../salescontext
|
||||
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
|
|
@ -18,7 +18,7 @@ asyncchecksuite "sales state 'proving'":
|
|||
|
||||
let slot = Slot.example
|
||||
let request = slot.request
|
||||
let proof = exampleProof()
|
||||
let proof = Groth16Proof.example
|
||||
|
||||
var market: MockMarket
|
||||
var clock: MockClock
|
||||
|
@ -29,7 +29,7 @@ asyncchecksuite "sales state 'proving'":
|
|||
setup:
|
||||
clock = MockClock.new()
|
||||
market = MockMarket.new()
|
||||
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(market: market, clock: clock, onProve: onProve.some)
|
||||
|
@ -53,11 +53,9 @@ asyncchecksuite "sales state 'proving'":
|
|||
|
||||
test "submits proofs":
|
||||
var receivedIds: seq[SlotId]
|
||||
var receivedProofs: seq[seq[byte]]
|
||||
|
||||
proc onProofSubmission(id: SlotId, proof: seq[byte]) =
|
||||
proc onProofSubmission(id: SlotId) =
|
||||
receivedIds.add(id)
|
||||
receivedProofs.add(proof)
|
||||
|
||||
let subscription = await market.subscribeProofSubmission(onProofSubmission)
|
||||
market.slotState[slot.id] = SlotState.Filled
|
||||
|
@ -67,7 +65,7 @@ asyncchecksuite "sales state 'proving'":
|
|||
market.setProofRequired(slot.id, true)
|
||||
await market.advanceToNextPeriod()
|
||||
|
||||
check eventually receivedIds == @[slot.id] and receivedProofs == @[proof]
|
||||
check eventually receivedIds == @[slot.id]
|
||||
|
||||
await future.cancelAndWait()
|
||||
await subscription.unsubscribe()
|
||||
|
|
|
@ -19,7 +19,7 @@ asyncchecksuite "sales state 'simulated-proving'":
|
|||
|
||||
let slot = Slot.example
|
||||
let request = slot.request
|
||||
let proof = exampleProof()
|
||||
let proof = Groth16Proof.example
|
||||
let failEveryNProofs = 3
|
||||
let totalProofs = 6
|
||||
|
||||
|
@ -29,14 +29,12 @@ asyncchecksuite "sales state 'simulated-proving'":
|
|||
var state: SaleProvingSimulated
|
||||
|
||||
var proofSubmitted: Future[void] = newFuture[void]("proofSubmitted")
|
||||
var submitted: seq[seq[byte]]
|
||||
var subscription: Subscription
|
||||
|
||||
setup:
|
||||
clock = MockClock.new()
|
||||
|
||||
proc onProofSubmission(id: SlotId, proof: seq[byte]) =
|
||||
submitted.add(proof)
|
||||
proc onProofSubmission(id: SlotId) =
|
||||
proofSubmitted.complete()
|
||||
proofSubmitted = newFuture[void]("proofSubmitted")
|
||||
|
||||
|
@ -45,7 +43,7 @@ asyncchecksuite "sales state 'simulated-proving'":
|
|||
market.setProofRequired(slot.id, true)
|
||||
subscription = await market.subscribeProofSubmission(onProofSubmission)
|
||||
|
||||
let onProve = proc (slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
|
||||
let onProve = proc (slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
|
||||
return success(proof)
|
||||
let context = SalesContext(market: market, clock: clock, onProve: onProve.some)
|
||||
agent = newSalesAgent(context,
|
||||
|
@ -79,9 +77,10 @@ asyncchecksuite "sales state 'simulated-proving'":
|
|||
|
||||
test "submits invalid proof every 3 proofs":
|
||||
let future = state.run(agent)
|
||||
let invalid = Groth16Proof.default
|
||||
|
||||
await market.waitForProvingRounds(totalProofs)
|
||||
check submitted == @[proof, proof, @[], proof, proof, @[]]
|
||||
check market.submitted == @[proof, proof, invalid, proof, proof, invalid]
|
||||
|
||||
await future.cancelAndWait()
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import ../helpers/always
|
|||
import ../examples
|
||||
|
||||
asyncchecksuite "Sales - start":
|
||||
let proof = exampleProof()
|
||||
let proof = Groth16Proof.example
|
||||
|
||||
var request: StorageRequest
|
||||
var sales: Sales
|
||||
|
@ -64,7 +64,7 @@ asyncchecksuite "Sales - start":
|
|||
return success()
|
||||
|
||||
queue = sales.context.slotQueue
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
|
||||
return success(proof)
|
||||
itemsProcessed = @[]
|
||||
request.expiry = ((await clock.now()) + 42).u256
|
||||
|
@ -112,7 +112,7 @@ asyncchecksuite "Sales - start":
|
|||
check sales.agents.any(agent => agent.data.requestId == request.id and agent.data.slotIndex == 1.u256)
|
||||
|
||||
asyncchecksuite "Sales":
|
||||
let proof = exampleProof()
|
||||
let proof = Groth16Proof.example
|
||||
|
||||
var availability: Availability
|
||||
var request: StorageRequest
|
||||
|
@ -167,7 +167,7 @@ asyncchecksuite "Sales":
|
|||
return success()
|
||||
|
||||
queue = sales.context.slotQueue
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
|
||||
return success(proof)
|
||||
await sales.start()
|
||||
itemsProcessed = @[]
|
||||
|
@ -369,7 +369,7 @@ asyncchecksuite "Sales":
|
|||
|
||||
test "handles errors during state run":
|
||||
var saleFailed = false
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
|
||||
# raise exception so machine.onError is called
|
||||
raise newException(ValueError, "some error")
|
||||
|
||||
|
@ -394,10 +394,10 @@ asyncchecksuite "Sales":
|
|||
test "generates proof of storage":
|
||||
var provingRequest: StorageRequest
|
||||
var provingSlot: UInt256
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
|
||||
provingRequest = slot.request
|
||||
provingSlot = slot.slotIndex
|
||||
return success(exampleProof())
|
||||
return success(Groth16Proof.example)
|
||||
createAvailability()
|
||||
await market.requestStorage(request)
|
||||
check eventually provingRequest == request
|
||||
|
@ -427,7 +427,7 @@ asyncchecksuite "Sales":
|
|||
test "calls onClear when storage becomes available again":
|
||||
# fail the proof intentionally to trigger `agent.finish(success=false)`,
|
||||
# which then calls the onClear callback
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!seq[byte]] {.async.} =
|
||||
sales.onProve = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.async.} =
|
||||
raise newException(IOError, "proof failed")
|
||||
var clearedRequest: StorageRequest
|
||||
var clearedSlotIndex: UInt256
|
||||
|
|
|
@ -14,6 +14,7 @@ asyncchecksuite "validation":
|
|||
let timeout = 5
|
||||
let maxSlots = 100
|
||||
let slot = Slot.example
|
||||
let proof = Groth16Proof.example
|
||||
let collateral = slot.request.ask.collateral
|
||||
|
||||
var validation: Validation
|
||||
|
@ -41,25 +42,25 @@ asyncchecksuite "validation":
|
|||
check validation.slots.len == 0
|
||||
|
||||
test "when a slot is filled on chain, it is added to the list":
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, @[], collateral)
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, proof, collateral)
|
||||
check validation.slots == @[slot.id]
|
||||
|
||||
for state in [SlotState.Finished, SlotState.Failed]:
|
||||
test "when slot state changes, it is removed from the list":
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, @[], collateral)
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, proof, collateral)
|
||||
market.slotState[slot.id] = state
|
||||
advanceToNextPeriod()
|
||||
check eventually validation.slots.len == 0
|
||||
|
||||
test "when a proof is missed, it is marked as missing":
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, @[], collateral)
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, proof, collateral)
|
||||
market.setCanProofBeMarkedAsMissing(slot.id, true)
|
||||
advanceToNextPeriod()
|
||||
await sleepAsync(1.millis)
|
||||
check market.markedAsMissingProofs.contains(slot.id)
|
||||
|
||||
test "when a proof can not be marked as missing, it will not be marked":
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, @[], collateral)
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, proof, collateral)
|
||||
market.setCanProofBeMarkedAsMissing(slot.id, false)
|
||||
advanceToNextPeriod()
|
||||
await sleepAsync(1.millis)
|
||||
|
@ -68,5 +69,5 @@ asyncchecksuite "validation":
|
|||
test "it does not monitor more than the maximum number of slots":
|
||||
for _ in 0..<maxSlots + 1:
|
||||
let slot = Slot.example
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, @[], collateral)
|
||||
await market.fillSlot(slot.request.id, slot.slotIndex, proof, collateral)
|
||||
check validation.slots.len == maxSlots
|
||||
|
|
|
@ -3,15 +3,21 @@ import std/options
|
|||
import pkg/ethers
|
||||
import pkg/codex/contracts/marketplace
|
||||
|
||||
const hardhatMarketAddress = Address.init("0x59b670e9fA9D0A427751Af201D676719a970857b").get()
|
||||
const hardhatMarketAddress =
|
||||
Address.init("0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44").get()
|
||||
const hardhatMarketWithDummyVerifier =
|
||||
Address.init("0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f").get()
|
||||
const marketAddressEnvName = "CODEX_MARKET_ADDRESS"
|
||||
|
||||
proc address*(_: type Marketplace): Address =
|
||||
proc address*(_: type Marketplace, dummyVerifier = false): Address =
|
||||
if existsEnv(marketAddressEnvName):
|
||||
without address =? Address.init(getEnv(marketAddressEnvName)):
|
||||
raiseAssert "Invalid env. variable marketplace contract address"
|
||||
|
||||
return address
|
||||
|
||||
hardhatMarketAddress
|
||||
if dummyVerifier:
|
||||
hardhatMarketWithDummyVerifier
|
||||
else:
|
||||
hardhatMarketAddress
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import ./time
|
|||
import ./deployment
|
||||
|
||||
ethersuite "Marketplace contracts":
|
||||
let proof = exampleProof()
|
||||
let proof = Groth16Proof.example
|
||||
|
||||
var client, host: Signer
|
||||
var marketplace: Marketplace
|
||||
|
@ -25,7 +25,8 @@ ethersuite "Marketplace contracts":
|
|||
client = ethProvider.getSigner(accounts[0])
|
||||
host = ethProvider.getSigner(accounts[1])
|
||||
|
||||
marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
|
||||
let address = Marketplace.address(dummyVerifier = true)
|
||||
marketplace = Marketplace.new(address, ethProvider.getSigner())
|
||||
|
||||
let tokenAddress = await marketplace.token()
|
||||
token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
|
||||
|
|
|
@ -31,11 +31,11 @@ asyncchecksuite "Deployment":
|
|||
|
||||
test "uses chainId hardcoded values as fallback":
|
||||
let deployment = Deployment.new(provider, configFactory())
|
||||
provider.chainId = 31337.u256
|
||||
provider.chainId = 167005.u256
|
||||
|
||||
let address = await deployment.address(Marketplace)
|
||||
check address.isSome
|
||||
check $(!address) == "0x59b670e9fa9d0a427751af201d676719a970857b"
|
||||
check $(!address) == "0x948cf9291b77bd7ad84781b9047129addf1b894f"
|
||||
|
||||
test "return none for unknown networks":
|
||||
let deployment = Deployment.new(provider, configFactory())
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import std/options
|
||||
import pkg/chronos
|
||||
import pkg/stew/byteutils
|
||||
import codex/contracts
|
||||
import ../ethertest
|
||||
import ./examples
|
||||
|
@ -8,7 +7,7 @@ import ./time
|
|||
import ./deployment
|
||||
|
||||
ethersuite "On-Chain Market":
|
||||
let proof = exampleProof()
|
||||
let proof = Groth16Proof.example
|
||||
|
||||
var market: OnChainMarket
|
||||
var marketplace: Marketplace
|
||||
|
@ -17,7 +16,8 @@ ethersuite "On-Chain Market":
|
|||
var periodicity: Periodicity
|
||||
|
||||
setup:
|
||||
marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
|
||||
let address = Marketplace.address(dummyVerifier = true)
|
||||
marketplace = Marketplace.new(address, ethProvider.getSigner())
|
||||
let config = await marketplace.config()
|
||||
|
||||
market = OnChainMarket.new(marketplace)
|
||||
|
@ -111,6 +111,9 @@ ethersuite "On-Chain Market":
|
|||
check (await market.willProofBeRequired(slotId(request.id, slotIndex))) == false
|
||||
|
||||
test "submits proofs":
|
||||
await market.requestStorage(request)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
await advanceToNextPeriod()
|
||||
await market.submitProof(slotId(request.id, slotIndex), proof)
|
||||
|
||||
test "marks a proof as missing":
|
||||
|
@ -260,20 +263,15 @@ ethersuite "On-Chain Market":
|
|||
await subscription.unsubscribe()
|
||||
|
||||
test "supports proof submission subscriptions":
|
||||
await market.requestStorage(request)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
await advanceToNextPeriod()
|
||||
var receivedIds: seq[SlotId]
|
||||
var receivedProofs: seq[seq[byte]]
|
||||
|
||||
proc onProofSubmission(id: SlotId, proof: seq[byte]) =
|
||||
proc onProofSubmission(id: SlotId) =
|
||||
receivedIds.add(id)
|
||||
receivedProofs.add(proof)
|
||||
|
||||
let subscription = await market.subscribeProofSubmission(onProofSubmission)
|
||||
|
||||
await market.submitProof(slotId(request.id, slotIndex), proof)
|
||||
|
||||
check receivedIds == @[slotId(request.id, slotIndex)]
|
||||
check receivedProofs == @[proof]
|
||||
|
||||
await subscription.unsubscribe()
|
||||
|
||||
test "request is none when unknown":
|
||||
|
|
|
@ -4,6 +4,7 @@ import std/times
|
|||
import std/typetraits
|
||||
|
||||
import pkg/codex/contracts/requests
|
||||
import pkg/codex/contracts/proofs
|
||||
import pkg/codex/sales/slotqueue
|
||||
import pkg/codex/stores
|
||||
|
||||
|
@ -62,8 +63,18 @@ proc example*(_: type SlotQueueItem): SlotQueueItem =
|
|||
let slot = Slot.example
|
||||
SlotQueueItem.init(request, slot.slotIndex.truncate(uint16))
|
||||
|
||||
proc exampleProof*(): seq[byte] =
|
||||
var proof: seq[byte]
|
||||
while proof.len == 0:
|
||||
proof = seq[byte].example
|
||||
return proof
|
||||
proc example(_: type G1Point): G1Point =
|
||||
G1Point(x: UInt256.example, y: UInt256.example)
|
||||
|
||||
proc example(_: type G2Point): G2Point =
|
||||
G2Point(
|
||||
x: [UInt256.example, UInt256.example],
|
||||
y: [UInt256.example, UInt256.example]
|
||||
)
|
||||
|
||||
proc example*(_: type Groth16Proof): Groth16Proof =
|
||||
Groth16Proof(
|
||||
a: G1Point.example,
|
||||
b: G2Point.example,
|
||||
c: G1Point.example
|
||||
)
|
||||
|
|
|
@ -23,7 +23,7 @@ type
|
|||
validators*: uint
|
||||
DebugNodes* = object
|
||||
client*: bool
|
||||
ethProvider*: bool
|
||||
provider*: bool
|
||||
validator*: bool
|
||||
topics*: string
|
||||
Role* {.pure.} = enum
|
||||
|
@ -48,15 +48,15 @@ proc init*(_: type StartNodes,
|
|||
StartNodes(clients: clients, providers: providers, validators: validators)
|
||||
|
||||
proc init*(_: type DebugNodes,
|
||||
client, ethProvider, validator: bool,
|
||||
client, provider, validator: bool,
|
||||
topics: string = "validator,proving,market"): DebugNodes =
|
||||
DebugNodes(client: client, ethProvider: ethProvider, validator: validator,
|
||||
DebugNodes(client: client, provider: provider, validator: validator,
|
||||
topics: topics)
|
||||
|
||||
template multinodesuite*(name: string,
|
||||
startNodes: StartNodes, debugNodes: DebugNodes, body: untyped) =
|
||||
|
||||
if (debugNodes.client or debugNodes.ethProvider) and
|
||||
if (debugNodes.client or debugNodes.provider) and
|
||||
(enabledLogLevel > LogLevel.TRACE or
|
||||
enabledLogLevel == LogLevel.NONE):
|
||||
echo ""
|
||||
|
@ -115,12 +115,12 @@ template multinodesuite*(name: string,
|
|||
"--bootstrap-node=" & bootstrap,
|
||||
"--persistence",
|
||||
"--simulate-proof-failures=" & $failEveryNProofs],
|
||||
debugNodes.ethProvider)
|
||||
debugNodes.provider)
|
||||
let restClient = newCodexClient(index)
|
||||
running.add RunningNode.new(Role.Provider, node, restClient, datadir,
|
||||
account)
|
||||
if debugNodes.ethProvider:
|
||||
debug "started new ethProvider node and codex client",
|
||||
if debugNodes.provider:
|
||||
debug "started new provider node and codex client",
|
||||
restApiPort = 8080 + index, discPort = 8090 + index, account
|
||||
|
||||
proc startValidatorNode() =
|
||||
|
|
|
@ -12,6 +12,11 @@ import ./multinodes
|
|||
logScope:
|
||||
topics = "test proofs"
|
||||
|
||||
# TODO: This is currently the address of the marketplace with a dummy
|
||||
# verifier. Use real marketplace address once we can generate actual
|
||||
# Groth16 ZK proofs.
|
||||
let marketplaceAddress = Marketplace.address(dummyVerifier = true)
|
||||
|
||||
twonodessuite "Proving integration test", debug1=false, debug2=false:
|
||||
let validatorDir = getTempDir() / "CodexValidator"
|
||||
|
||||
|
@ -22,7 +27,7 @@ twonodessuite "Proving integration test", debug1=false, debug2=false:
|
|||
client.getPurchase(id).option.?state == some state
|
||||
|
||||
setup:
|
||||
marketplace = Marketplace.new(Marketplace.address, ethProvider)
|
||||
marketplace = Marketplace.new(marketplaceAddress, ethProvider)
|
||||
period = (await marketplace.config()).proofs.period.truncate(uint64)
|
||||
|
||||
# Our Hardhat configuration does use automine, which means that time tracked by `ethProvider.currentTime()` is not
|
||||
|
@ -110,7 +115,7 @@ twonodessuite "Proving integration test", debug1=false, debug2=false:
|
|||
|
||||
multinodesuite "Simulate invalid proofs",
|
||||
StartNodes.init(clients=1'u, providers=0'u, validators=1'u),
|
||||
DebugNodes.init(client=false, ethProvider=false, validator=false):
|
||||
DebugNodes.init(client=false, provider=false, validator=false):
|
||||
|
||||
proc purchaseStateIs(client: CodexClient, id: PurchaseId, state: string): bool =
|
||||
client.getPurchase(id).option.?state == some state
|
||||
|
@ -120,7 +125,7 @@ multinodesuite "Simulate invalid proofs",
|
|||
var slotId: SlotId
|
||||
|
||||
setup:
|
||||
marketplace = Marketplace.new(Marketplace.address, ethProvider)
|
||||
marketplace = Marketplace.new(marketplaceAddress, ethProvider)
|
||||
let config = await marketplace.config()
|
||||
period = config.proofs.period.truncate(uint64)
|
||||
slotId = SlotId(array[32, byte].default) # ensure we aren't reusing from prev test
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b5f33992b67df3733042a7d912c854700e8c863c
|
||||
Subproject commit 6c9f797f408608958714024b9055fcc330e3842f
|
Loading…
Reference in New Issue