nim-codex/codex/contracts
markspanbroek 29433bad9a
Fix concurrency issues (#993)
* Use http subscriptions instead of websocket for tests

To work around this issue when subscriptions are
inactive for more than 5 minutes:
https://github.com/NomicFoundation/hardhat/issues/2053

Use 100 millisecond polling; default polling interval
of 4 seconds is too close to the 5 second timeout for
`check eventually`.

* use .confirm(1) instead of confirm(0)

confirm(0) doesn't wait at all, confirm(1) waits
for the transaction to be mined

* speed up partial payout integration test

* update nim-ethers to version 0.10.0

includes fixes for http polling and .confirm()

* fix timing of marketplace tests

allow for a bit more time to withdraw funds

* use .confirm(1) in marketplace tests

to ensure that the transaction has been processed
before continuing with the test

* fix timing issue in validation unit test

* fix proof integration test

there were two logic errors in this test:
- a slot is freed anyway at the end of the contract
- when starting the request takes a long time, the
  first slot can already be freed because there were
  too many missing proofs

* fix intermittent error in contract tests

currentTime() doesn't always correctly reflect
the time of the next transaction

* reduce number of slots in integration test

otherwise the windows runner in the CI won't
be able to start the request before it expires

* fix timing in purchasing test

allow for a bit more time for a request to
be submitted

* fix timing of request submission in test

windows ci is so slow, it can take up to 40 seconds
just to submit a storage request to hardhat

* increase proof period to 90 seconds

* adjust timing of integration tests

reason: with the increased period length of 90 seconds, it
can take longer to wait for a stable challenge at the
beginning of a period.

* increase CI timeout to 2 hours

* Fix slow builds on windows

apparently it takes windows 2-3 seconds to
resolve "localhost" to 127.0.0.1 for every
json-rpc connection that we make 🤦
2024-11-25 11:23:04 +00:00
..
interactions feat: create logging proxy (#663) 2024-01-22 23:35:03 -08:00
Readme.md feat: collateral per slot (#390) 2023-04-14 11:04:17 +02:00
clock.nim Cleanup integration tests (#757) 2024-05-23 15:29:30 +00:00
config.nim chore: add `downtimeProduct` config parameter (#867) 2024-08-15 02:31:02 +00:00
deployment.nim chore: update testnet marketplace address (#983) (#984) 2024-11-04 00:41:03 +00:00
interactions.nim Validator (#387) 2023-04-19 15:06:00 +02:00
market.nim Fix concurrency issues (#993) 2024-11-25 11:23:04 +00:00
marketplace.nim chore: update dependencies, especially nim-ethers to chronos v4 compatible version (#968) 2024-10-30 10:40:17 +00:00
proofs.nim Update contract (#734) 2024-03-12 08:18:25 +00:00
requests.nim API tweaks for OpenAPI, errors and endpoints (#886) 2024-09-24 08:37:08 +00:00

Readme.md

Codex Contracts in Nim

Nim API for the Codex smart contracts.

Usage

For a global overview of the steps involved in starting and fulfilling a storage contract, see Codex Contracts.

Smart contract

Connecting to the smart contract on an Ethereum node:

import codex/contracts
import ethers

let address = # fill in address where the contract was deployed
let provider = JsonRpcProvider.new("ws://localhost:8545")
let marketplace = Marketplace.new(address, provider)

Setup client and host so that they can sign transactions; here we use the first two accounts on the Ethereum node:

let accounts = await provider.listAccounts()
let client = provider.getSigner(accounts[0])
let host = provider.getSigner(accounts[1])

Storage requests

Creating a request for storage:

let request : StorageRequest = (
  client:           # address of the client requesting storage
  duration:         # duration of the contract in seconds
  size:             # size in bytes
  contentHash:      # SHA256 hash of the content that's going to be stored
  proofProbability: # require a storage proof roughly once every N periods
  maxPrice:         # maximum price the client is willing to pay
  expiry:           # expiration time of the request (in unix time)
  nonce:            # random nonce to differentiate between similar requests
)

When a client wants to submit this request to the network, it needs to pay the maximum price to the smart contract in advance. The difference between the maximum price and the offered price will be reimbursed later.

Once the payment has been prepared, the client can submit the request to the network:

await storage
  .connect(client)
  .requestStorage(request)

Storage offers

Creating a storage offer:

let offer: StorageOffer = (
  host:       # address of the host that is offering storage
  requestId:  request.id,
  price:      # offered price (in number of tokens)
  expiry:     # expiration time of the offer (in unix time)
)

Hosts submits an offer:

await storage
  .connect(host)
  .offerStorage(offer)

Client selects an offer:

await storage
  .connect(client)
  .selectOffer(offer.id)

Starting and finishing a storage contract

The host whose offer got selected can start the storage contract once it received the data that needs to be stored:

await storage
  .connect(host)
  .startContract(offer.id)

Once the storage contract is finished, the host can release payment:

await storage
  .connect(host)
  .finishContract(id)

Storage proofs

Time is divided into periods, and each period a storage proof may be required from the host. The odds of requiring a storage proof are negotiated through the storage request. For more details about the timing of storage proofs, please refer to the design document.

At the start of each period of time, the host can check whether a storage proof is required:

let isProofRequired = await storage.isProofRequired(offer.id)

If a proof is required, the host can submit it before the end of the period:

await storage
  .connect(host)
  .submitProof(id, proof)

If a proof is not submitted, then a validator can mark a proof as missing:

await storage
  .connect(validator)
  .markProofAsMissing(id, period)