Skip to content

Releases: Project-DSpace/darwin-client

darwin v0.3.11+ds.9

Choose a tag to compare

@github-actions github-actions released this 31 Oct 02:55

The darwin client v0.3.11+ds.9 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from 642ad06 by the Build workflow (run 41160917907), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release gives the listings producer, darwin listings serve, three options its operator can set: a hide list of addresses it publishes nothing of, a hold that keeps each new readable entry out of the published files until its address is on a cleared list, and a held file listing the entries held. Hiding an entry that is already published starts the feed again under a new epoch without it, so the hidden address appears in no record; a new epoch can also be asked for with the new darwin listings new-epoch. Every option is off by default: a producer started as before publishes as before. Payments, uploads, downloads and the listings nodes serve are unchanged; the options change only what one producer publishes, and the network keeps every file and listing whatever its lists say.

Producer operators: the hide list

  • --hide-list FILE. An address on the list has no entry, support, row in rejected.json or feed record in any published file, nor in their .gz and .zst copies, and the producer stops reading its file's root map for a size.
  • The format. One address per line, as 64 hex digits in any case, with or without 0x; # starts a comment, to the end of its line, and blank lines are ignored; a UTF-8 byte order mark at the start of the file is skipped. A line that isn't an address is skipped, and only its line number is logged. The cleared list uses the same format.
  • When it is read. The file is looked at again at the start of each cycle and before each publication between cycles, and read again when its modification time or length changes. The last list read is kept in the producer's state: while the file is missing or can't be read, that list stays, after a restart too, with one warning for as long as it stays so.
  • Taking an address off. An address removed from the list is decided again at the next cycle and, if listed, published at its own key, keeping its listing time, without waiting for the daily sweep. A producer started without --hide-list has an empty list, so what it hid before is published again.

Producer operators: holding until cleared

  • --hold-until-cleared FILE (needs --held-file). Each readable entry the producer hasn't published yet is held: it is published in no file until its address is on the cleared list. Once it is, the next cycle publishes it at its own key, keeping its listing time, with a feed put whose at is that publication's start. Like any late arrival, it may sit below head.json's newest 50.
  • What stays. Entries published before the option is set stay published, and entries that aren't readable are never held. An address on both lists is hidden. While entries are held, a readable address neither cleared nor published has no row in rejected.json, held or not.
  • Turning it off. A producer started again without the option publishes the entries it held.

Producer operators: the held file

  • --held-file FILE, never inside the out dir (the producer refuses one there). It is written whole at every cycle, replacing the file at once, and on Unix is readable by its owner and group only.
  • What it holds. {"generated_at": …, "lists_read_at": …, "held": [...]}: each entry held as its address's shard holds it, with its data_map, plus held_since, when the publication that first held it began. lists_read_at is the start of the latest cycle at which every list given could be read, changed or not, so whatever reads the file can tell how current the lists behind it are. Times are Unix seconds.
  • How long an entry stays. An entry stays in the file, with the same held_since, across cycles and restarts, until it is published, hidden, no longer served by the network, or rejected when its address is decided again.

Producer operators: new epochs

  • darwin listings new-epoch --state-dir DIR leaves a request, the file new-epoch in the producer's state directory. The producer's next cycle (or its first after it starts again) takes it once and removes it. It needs no network, wallet or chain, and a directory without the producer's listings.mdb is refused.
  • On hiding a published entry. At the start of each cycle, once the lists are read, the producer starts a new epoch if an address on the hide list has an entry published. It checks what it holds, not what changed, so a list changed between cycles, or while the producer was stopped, is acted on too.
  • What a new epoch does. The feed starts again at record 1, with one put for each entry kept (every published entry whose address isn't hidden), in key order, all with at set to when the epoch began. An entry not kept leaves the index with its support, and no record of the new epoch names it. Every file is written again, status.json last. Then the time pages the new pages.json doesn't list, with their copies, and the feed segments past the new last one are deleted at once, not a day later. A producer stopped part way finishes the deleting at its next start once the new epoch's files are in place, or after its next publication writes them. A producer started without its state over an out dir holding an earlier feed's files deletes them the same way once its first publication's files are in place. Entries held stay held.
  • Readers. A reader following the feed sees the new epoch in head.json, or finds a page or segment missing, and starts again from pages.json, as docs/listings-v1.md has always said. No new removal reason is added.

Library users

  • ServeOptions (hide_list, cleared_list, held_file, all None by default), exported from ant_core::data, is given to the new Producer::open_with; Producer::open takes the defaults. A cleared list without a held file, or a held file inside the out dir, is refused.
  • Producer::request_new_epoch leaves the request darwin listings new-epoch leaves (listings_serve::NEW_EPOCH_FILE), and Publisher::start_epoch starts a new epoch keeping the entries a given function keeps.
  • ant_core::data::client::listings_lists holds the lists' format (AddressList::parse) and the held file's types.
  • As before, a project that uses ant-core takes both lines below, with the same tag:
[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.9" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.9" }

What stays

  • Payments and their prices, size classes, the band, the price ceiling, V2 occupied-quote handling, the merkle refusal, payment recovery, kept proofs, root payments by default and envelopes by default, with all five payment fields shown, are those of v0.3.11+ds.8.
  • The listings/v1/ format is unchanged: the files, their fields and the feed's records are as before. Without the new options and without a new-epoch request, the producer publishes as v0.3.11+ds.8 does.

Releases

  • It is tested against darwin-node 0.23.0+ds.20 (363640e), the revision its tests pin.
  • This release is published only after the tagged commit's Test run and its Build run on main both passed.
  • Going back to v0.3.11+ds.8 on the same state is a re-pin of both lines' tags, or a binary swap, under the usual pause. That version doesn't read the lists or the held entries, so it publishes again what they hid or held as it decides those addresses again, and it doesn't finish a new epoch's deleting that a stop cut short.

darwin v0.3.11+ds.8

Choose a tag to compare

@github-actions github-actions released this 29 Oct 18:45

The darwin client v0.3.11+ds.8 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from d1475d1 by the Build workflow (run 40931336310), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release makes root payments the library's default: a Client given a wallet now pays through the root vault, DarwinRootVault at 0xf6675722f443132b045857546E57e35360Bb03b9 on Robinhood Chain, as the darwin CLI has since v0.3.11+ds.7, and Client::without_root_payments keeps today's payment vault, 0xE0f627986c0438De0E314B644f3C8aA21D3f4f6B (Dan, 2026-10-29, on the recommendation that the library pay the root vault by default with an opt-out, outside payers staying opt-in: "do what you recommend"). An outside payer can also name its own address, so a root wave its wallet re-sent is counted in the upload's cost, and the CLI's readers on a devnet now read the root vault its manifest names. How root payments work, their proofs, the split between both vaults and the envelope rules are those of v0.3.11+ds.6; prices are those of v0.3.11+ds.5.

Library users: root by default after upgrading

  • A client built from a key now pays the root vault. After upgrading, a Client given a wallet (Client::with_wallet) pays every payment, of uploads, single records and pointers alike, through the root vault wherever the root vault of its network is known: the bundled network's, built in, or the one the client names with Client::with_root_vault (before or after with_wallet; on a devnet, the vault ant_core::config::manifest_root_vault reads from its manifest). Each payment's records go as one wave in one transaction, which the vault records once, and each record's proof (tag 0x03) shows its place in the wave. Where no root vault is known, the client pays the payment vault, as before. This applies to native builds; the browser core is unchanged.
  • One approval first. The first root payment of a wallet is preceded by a one-time approval letting the root vault spend the wallet's DARWIN (one transaction, paid in gas), as the payment vault needed its own when the wallet first paid there. After that, payments cost the same tokens as before.
  • The root vault's wallet comes from the client's wallet. The client pays the root vault through a wallet made from its wallet's own signer (ant_core::config::root_wallet_of), on the same RPC and token and signing for the same chain: the wallet Client::with_root_payments_from_key makes from the key. A wallet built from a Wallet value works as well as one built from a key string; no key needs to be passed again.
  • Opting out. Client::without_root_payments() keeps every payment on the payment vault, one record kept by the vault for each quote paid, before or after with_wallet. The client still reads root-paid records at its network's root vault, or the one it names. Client::with_root_payments and Client::with_root_payments_from_key opt back in.
  • A wallet with its own transaction settings stays on the payment vault. A wallet whose transaction settings were changed (Wallet::set_transaction_config, such as a gas cap) can't have them read and carried over to a second wallet, so such a client makes no root vault's wallet by default and pays the payment vault, as before. Give it one configured as its own with Client::with_root_payments to pay the root vault.
  • Root payments wait for the wallet's lock. The root vault's wallet has a lock of its own (every Wallet::new makes one), so in native builds every payment the client sends, through either vault, also holds the lock of the wallet given to with_wallet (Wallet::lock, shared by its clones), after the key's lock the client keeps and before the root vault's wallet's own. A caller that pays with a clone of its wallet under that lock, as evmlib asks of a shared wallet, is serialised with the client's default root payments, as it was with per-quote payments, so the two never sign the same nonce. Don't call the client's payment methods while holding that lock. Merkle batch payments (Client::pay_for_merkle_batch) and the in-memory adapter's are now sent under the same locks. Browser builds (without the native feature) make no root payments; their pay_for_storage and single-node payments take no lock, as before.
  • A root vault's wallet the caller gives is kept. Client::with_root_payments still takes a wallet of the caller's own, checked as in v0.3.11+ds.6, and a later with_wallet keeps it while it still matches.
  • Checkpoints and interrupted uploads. An upload that plans a root payment writes its checkpoint in the second format of v0.3.11+ds.6, which v0.3.11+ds.5 and older clients refuse. A journaled root payment is reconciled only by a client paying the root vault, so resume an interrupted upload without without_root_payments.

Outside payers: their own address

  • UploadPayer::payer_address (none by default) names the address a payer's transactions are sent from. It matters only when another transaction than the one the payer returned recorded a root wave it paid: a recording sent from that address (its wallet sped the payment up or replaced it) counts the wave's total in the upload's cost, whether or not the returned transaction can still be read; one sent from another address, or whose sender can't be read, counts as moving no tokens. A payer that names none is counted as in v0.3.11+ds.7, by comparing the senders of both transactions.
  • Accounting only. What is paid, and whether a wave counts as paid, are unchanged.
  • Root waves stay opt-in for outside payers: UploadPayer::pays_root_waves stays false by default. Prepared uploads and chunks finished with finalize_upload* or finalize_chunk, other external signers, and a pointer's state quoted with prepare_pointer_payment stay on the payment vault, at settlement version 2, under the new default as well.

CLI users: devnet readers

  • darwin listings, darwin listing and darwin file verify read the root vault a devnet's manifest names (evm.root_vault_address), beside the payment vault, so they see records the CLI paid there. A manifest naming none reads the payment vault alone. On the bundled network they read both vaults, as before.
  • Payments are unchanged. The CLI pays through the root vault by default, now through the library's default; --no-root-payments calls Client::without_root_payments, and a warning still says so when no root vault is known.

What stays

  • Size classes, the band, the price ceiling, V2 occupied-quote handling, the merkle refusal, payment recovery, kept proofs and envelopes by default, with all five payment fields shown, are unchanged.
  • Payment reads on the bundled network keep the order of v0.3.11+ds.6: PublicNode, then Robinhood's RPC, then ordofi last.

Library users: ant-protocol from the same tag

As before, a project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.8" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.8" }

Releases

  • It is tested against darwin-node 0.23.0+ds.20 (363640e), the revision its tests pin, which settles under version 3.
  • This release is published only after the tagged commit's Test run and its Build run on main both passed.
  • Going back to v0.3.11+ds.7 is a re-pin of both lines' tags under the usual pause; a library client then pays the payment vault until it opts in with with_root_payments or with_root_payments_from_key, and an outside payer's payer_address is no longer called. An upload checkpoint holding a root payment is resumed there only by a client paying the root vault.

darwin v0.3.11+ds.7

Choose a tag to compare

@github-actions github-actions released this 29 Oct 14:53

The darwin client v0.3.11+ds.7 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from f8fcfa9 by the Build workflow (run 40893358298), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release is the root switch: the darwin CLI now pays through the root vault, DarwinRootVault at 0xf6675722f443132b045857546E57e35360Bb03b9 on Robinhood Chain, by default, and an outside payer (UploadPayer) can ask to pay root waves too. The library's default stays today's payment vault, 0xE0f627986c0438De0E314B644f3C8aA21D3f4f6B. How root payments work, their proofs, the split between both vaults and the envelope rules are those of v0.3.11+ds.6. Prices are those of v0.3.11+ds.5: quotes on either price curve are accepted.

CLI users: root by default

  • Every paying command pays through the root vault unless told otherwise: each payment's records as one wave in one transaction, which the vault records once, and each record's proof (tag 0x03) showing its place in the wave.
  • --no-root-payments pays through the payment vault only, as v0.3.11+ds.6 did without a flag. --root-payments, the opt-in of v0.3.11+ds.6, still parses (hidden from the help) so commands written for it keep running; it asks for the default, and it can't be given with --no-root-payments. Commands that don't pay no longer refuse it.
  • Falling back, record by record. A record whose close group holds a node that can't check a root proof is quoted again for the payment vault and paid there, as in v0.3.11+ds.6. A payment pays both vaults only when at least 3 of its records pay the root vault; with fewer, they all pay the payment vault. The envelope never pays alone in either payment where moving records can avoid it, and records kept from an interrupted attempt never move.
  • On a devnet (--evm-network local), the CLI reads and pays the root vault the devnet's manifest names (evm.root_vault_address). A manifest that names none leaves the CLI paying the payment vault, with a warning, instead of stopping. A manifest that names its own payment vault (evm.payment_vault_address) as its root vault is refused. On a devnet, darwin listings, darwin listing and darwin file verify still read the payment vault alone, so they don't see root-paid records there; on the bundled network they read both vaults, as since v0.3.11+ds.3.
  • Checkpoints. An upload checkpoint holding a root plan or proof is written in the second format of v0.3.11+ds.6, which v0.3.11+ds.5 and older clients refuse. With root payments the default, an interrupted CLI upload that planned a root payment writes it. A journaled root payment is reconciled only by a client paying the root vault, so resume an interrupted upload without --no-root-payments.

Library users: opt in, now in one call

  • The default stays the payment vault. A Client pays through the root vault only once it opts in: Client::with_root_payments takes a root vault's wallet as before, and the new Client::with_root_payments_from_key(network, private_key, chain_id) sets the client's wallet and its root vault's wallet from one key in one call. It pays the bundled network's root vault, or the one the client names, and is refused when no root vault is known.
  • Naming a root vault without paying it. Client::with_root_vault(address) names the root vault a client reads (Client::root_vault returns it): its envelope records and its read-backs of payments read that vault beside the payment vault, with no root vault's wallet needed. On the bundled network its built-in vault is read either way. On a devnet, ant_core::config::manifest_root_vault reads the vault a manifest names, and ListingChain::new_with_root_vault(network, root_vault) builds a reader of both vaults; ListingChain::new is that reader naming none.
  • The payment vault is never a root vault. Once a client's payment network is known (with_wallet or with_evm_network), with_root_vault given that network's payment vault leaves the client as it was, with a warning. A root vault named before the network is known is dropped, with a warning, when with_wallet or with_evm_network sets a network whose payment vault it is. manifest_root_vault returns an error for a manifest whose root vault is its payment vault.

Outside payers: root waves when asked

  • UploadPayer::pays_root_waves (false by default). A payer that returns true is quoted for the root vault wherever every quoter of a record accepts root proofs and the client knows its network's root vault, built in or named with with_root_vault; no root vault's wallet is needed in the client. Its other records come in plans for the payment vault, in a payment of their own. A payer that doesn't ask is quoted for the payment vault alone, as before.
  • Paying a wave. Each call to pay_quotes holds plans for one vault (ChunkPaymentPlan::vault). For the root vault, the payer sends one payForQuotes call to the root vault with the non-zero quotes' leaves in ascending quote-hash order, and returns the transaction for every paid quote and the plans' total. Asked again for a payment it already made, after a restart or an error, it returns that payment and sends nothing.
  • What the client does next. It reads the root vault's record of the wave, finds the transaction that recorded it, and builds each record's proof naming that transaction. That is the payer's own when its receipt shows the recording; otherwise the vault's RootPaid log names it, also when the transaction the payer returned reverted and a later one recorded the wave, or never gets a receipt, as when the payer's wallet sped it up or replaced it. A wave recorded by another transaction is taken as paid by that one, and nothing is paid again. When that transaction was sent from the same address as the one the payer returned (its wallet's replacement), the upload's cost counts the wave's total; when it was sent from another address, or the senders can't be read, the payment counts as moving no tokens. A root request the payer paid at the payment vault instead is stored with single-node proofs (tag 0x01) once that vault records every quote. Where neither vault shows the payment, the upload stops with the payment kept pending, and the next attempt asks the payer again.
  • UploadPayer::exact_approvals (true by default) says the payer approves each vault for exactly what it pays, so paying both vaults in one upload costs a second approval: its payments then pay both vaults only from 5 records that can pay the root vault, and with fewer all pay the payment vault. A payer that approves without limit returns false and gets the client's threshold of 3.

What stays on the payment vault

External signers pay quotes one by one, so what they pay is quoted for the payment vault, at settlement version 2, whatever vault the client pays: prepared uploads (file_prepare_upload, data_prepare_upload) finished with finalize_upload*, prepared chunks (prepare_chunk_payment, prepare_chunk_payment_plan) finished with finalize_chunk or an external signer, a pointer's state quoted with prepare_pointer_payment, and the browser core.

What stays

  • Size classes, the band, the price ceiling, V2 occupied-quote handling, the merkle refusal, payment recovery, kept proofs and envelopes by default, with all five payment fields shown, are unchanged.
  • Payment reads on the bundled network keep the order of v0.3.11+ds.6: PublicNode, then Robinhood's RPC, then ordofi last.

Library users: ant-protocol from the same tag

As before, a project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.7" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.7" }

Releases

  • It is tested against darwin-node 0.23.0+ds.20 (363640e), the revision its tests pin, which settles under version 3.
  • This release is published only after the tagged commit's Test run and its Build run on main both passed.
  • Going back to v0.3.11+ds.6 is a re-pin of both lines' tags under the usual pause; the CLI then pays the payment vault unless given --root-payments. An upload checkpoint holding a root payment is resumed by v0.3.11+ds.6 only with --root-payments, and can't be resumed by v0.3.11+ds.5.

darwin v0.3.11+ds.6

Choose a tag to compare

@github-actions github-actions released this 28 Oct 16:45

The darwin client v0.3.11+ds.6 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from a65b1bf by the Build workflow (run 40726101387), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release is root payments: a client can pay through the root vault, DarwinRootVault at 0xf6675722f443132b045857546E57e35360Bb03b9 on Robinhood Chain, which keeps one record for a whole wave of records instead of one for each paid quote. It is opt-in: darwin --root-payments, or Client::with_root_payments in the library. Without it, every payment goes through today's payment vault, 0xE0f627986c0438De0E314B644f3C8aA21D3f4f6B, as in v0.3.11+ds.5, and that vault stays the default. Prices are those of v0.3.11+ds.5: quotes on either price curve are accepted.

Paying through the root vault

  • One wave a payment. A payment's records are paid as one wave in one transaction (payForQuotes, the same call the payment vault takes). Each record's paid quote is a leaf, in ascending quote-hash order, and the vault records the wave once, under the key its tree gives, with a RootPaid log.
  • Each record's proof (proof tag 0x03) carries its quotes, the transaction and its leaf: the paid quote's hash, the amount, the leaf's place in the wave, the wave's leaf count and its path. A storer hashes the leaf up the path to the wave's key and reads the vault's record under it.
  • Quotes and amounts. A record paid through the root vault is quoted at settlement version 3, which means root proofs are accepted; a record paid through the payment vault is quoted at version 2, as before. Either way a record pays its size class's amount: 3 × the median quote × its weight.
  • Reading back. Once the payment is confirmed, the client reads the vault's record of the wave back. If the wave's own transaction reverted or was replaced and the vault records the wave as paid by another transaction, its records are taken as paid, and their proofs name the transaction that recorded it, found by its RootPaid log. If that transaction can't be found, the payment fails with its journal kept, and a retry looks again without paying again.
  • Envelopes. An upload's envelope is paid in the same wave as its content. Its payment record, with all five fields shown by default, is checked against the root vault and names the transaction that recorded the wave.
  • One signer at a time. A root payment and a payment-vault payment from one key go through two wallets, so the client holds one lock for each key in the process while it signs and sends: the upload engine's journaled payments (a payment of its own, the lead of a shared wave and the reconciling of one), Client::batch_pay, the in-memory upload adapter's per-chunk payment and Client::approve_token_spend. The upload engine's journaled payments also hold the paying wallet's own lock (Wallet::lock) inside the key's lock, as before root payments. The root wallet is a Wallet of its own (ant_core::config::root_wallet builds a new one), with its own lock, so a library caller that shares the client's payment wallet and takes its Wallet::lock doesn't keep root payments out. Three paths send without the key's lock, as before root payments: merkle batch payments (Client::pay_for_merkle_batch's single batches and the in-memory adapter's pay_merkle), which the network's nodes refuse, and SingleNodeQuotePayment::pay, a raw building block. Separate processes with one key are not serialised.

Falling back, record by record

  • A record whose close group holds a node that can't check a root proof (one that answers StorerUpdateRequired at settlement version 3) is quoted again for today's payment vault and paid there. The client remembers such nodes, so later records near them are quoted for the payment vault at once.
  • A payment uses both vaults only when that saves gas: when at least 3 of its records pay the root vault. Otherwise all of its records pay the payment vault. The envelope never pays alone in either payment: where the payment vault's payment would hold it alone, one record quoted for the root vault joins it if at least 3 are left there, and otherwise they all move. Records kept from an interrupted attempt never move, whatever these rules would give: they pay the vault that attempt chose, since its transaction may already have been sent.
  • Payers and external signers pay quotes one by one, so what they pay is quoted for the payment vault, at settlement version 2, even on a client paying the root vault: uploads through an UploadPayer (file_upload_with_payer), prepared uploads (file_prepare_upload, data_prepare_upload, prepare_chunk_payment, prepare_chunk_payment_plan) finished with finalize_upload, finalize_chunk or an external signer, a pointer's state quoted with prepare_pointer_payment, and the browser core.

What stays

  • Today's payment vault is the default and the network's identity. Without --root-payments, quotes, payments, checkpoints and payment records are as in v0.3.11+ds.5.
  • Prices: every quote check accepts a price on the current curve or on the earlier ×32 curve, as in v0.3.11+ds.5.
  • Size classes, the band, the price ceiling, occupied-quote handling, the merkle refusal, payment recovery and envelopes by default are unchanged.

Payment reads: ordofi last

On the bundled network, reads of payments (receipts, blocks and vault records) now go to PublicNode first, then Robinhood's own RPC, then ordofi. Log queries go to Robinhood's RPC first, then PublicNode, then ordofi, as before. ordofi stays as the last fallback rather than being dropped (Dan, 2026-10-28: "just keep it as last fallback"), and goes last for ordinary reads too (Dan, 2026-10-28: "Make it last"): it answered payment reads "busy" or too late in October 2026, and still answered -32005 "busy" on 28 October.

Checkpoints

An upload checkpoint holding a plan or a proof for the root vault is written in a second format, which v0.3.11+ds.5 and older clients refuse whole; a checkpoint with neither is written as before. A journaled root payment is reconciled only by a client paying the root vault, so an interrupted --root-payments upload is resumed with --root-payments.

Readers

Every reader of payments since v0.3.11+ds.3 reads the root vault beside the payment vault, so listings and upload proofs of root-paid records check out with them. Older readers (older CLIs, older darwin-listings, other indexers) report them as unpaid until they update.

Library users: ant-protocol from the same tag

As before, a project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.6" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.6" }

Client::with_root_payments takes a wallet of the same key as with_wallet's that pays the root vault (ant_core::config::root_wallet builds one); on the bundled network it must pay the root vault the configuration names. The vendored ant-protocol has root payment proofs (PROOF_TAG_ROOT, ProofType::Root, RootPaymentProof, serialize_root_proof, deserialize_root_proof) and CURRENT_SETTLEMENT_VERSION 3. ProofType is no longer #[non_exhaustive], so a match on it names each type.

Releases

  • It is tested against darwin-node 0.23.0+ds.20 (363640e), the revision its tests pin, which settles under version 3.
  • This release is published only after the tagged commit's Test run and its Build run on main both passed.
  • Going back to v0.3.11+ds.5 is a re-pin of both lines' tags under the usual pause. An upload checkpoint written with root payments can't be resumed by v0.3.11+ds.5: finish it with this client first.

darwin v0.3.11+ds.5

Choose a tag to compare

@github-actions github-actions released this 28 Oct 09:56

The darwin client v0.3.11+ds.5 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from 2840054 by the Build workflow (run 40664550446), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release is the price curve: storage costs 100 DARWIN per GB of full chunks on empty nodes, and the price rises to 10 times that at 2,000 records instead of 6,000 (Dan, 2026-10-28 decision: "make chunk price 100 darwin = 1gb", "update to D = 2000"). Every quote check accepts a price on this curve or on the earlier one, so the client works with nodes on either side of the nodes' switch. How payments are made, read back and recovered doesn't change. It is tested against darwin-node 5d97082 (darwin-node's main after 0.23.0+ds.19, adding only the price curve that 0.23.0+ds.20 ships), the revision its tests pin, whose vendor/ant-protocol this release's copy matches.

The curve

  • The formula is upstream's, price = BASELINE + 9 × BASELINE × (n / D)², with n the records a node stores (its weight total, in full chunks). The vendored calculate_price now has BASELINE = 0.1396736 DARWIN (139,673,600,000,000,000 wei) and D = 2,000.

  • Where the baseline comes from: 100 DARWIN per 10^9 bytes × 4,190,208 bytes per full chunk ÷ 3 (a chunk pays 3 × the median quote) = 0.1396736 DARWIN per quote at an empty node. The coefficient stays 9 × the baseline, as upstream, and D = 2,000 makes the price reach 10 × the baseline at 2,000 records.

    Records stored Price per quote Per full chunk Per GB of full chunks Earlier curve, per GB
    0 0.1396736 DARWIN 0.4190208 DARWIN 100 DARWIN about 89.5 DARWIN (0.125 a quote, 0.375 a chunk)
    2,000 1.396736 DARWIN 4.190208 DARWIN 1,000 DARWIN about 179 DARWIN
    6,000 11.4532352 DARWIN 34.3597056 DARWIN 8,200 DARWIN about 895 DARWIN
  • Records by size pay as in v0.3.11+ds.4: 3 × the median quote × the weight of the record's size class. A 16 KiB-class chunk costs 0.1059828 DARWIN on empty nodes, so the README's 8 KB example, four such chunks, costs 0.4239 DARWIN.

  • The earlier curve is upstream's constants × 32 (BASELINE 0.125 DARWIN, D 6,000), what nodes to darwin-node 0.23.0+ds.19 quote. It stays in the vendored crate as calculate_price_legacy, with price_band_legacy and price_in_band_legacy beside it; price_in_band checks the new curve's band only, and price_in_accepted_band accepts a price in either curve's band.

Accepting both curves

  • Every quote check (native, merkle and browser) accepts a price in the band of the new curve or in the same band of the earlier curve: from the curve's price at floor(259/1,024 × K) to its price at K, for K committed records. Each band is checked whole, exactly as in v0.3.11+ds.4; a price between the two bands, or past both, is refused, and its error names both bands.
  • So quotes from every node pass: darwin-node 0.23.0+ds.19 nodes, which quote the earlier curve; darwin-node 0.23.0+ds.20 nodes before their switch, which quote the earlier curve too; and ds.20 nodes after it, which quote the new curve.

What stays

Settlement version 2, the size classes and their weights, the band, the price ceiling, occupied-quote handling, the merkle refusal, payment recovery, saved-upload restore and envelopes by default are as in v0.3.11+ds.4. darwin file cost prices from live quotes, so it shows the earlier prices before the switch and the new ones after it.

The price ceiling

--max-chunk-price (and ClientConfig::max_chunk_price) still compares a quote's price, and there is still no default ceiling. A ceiling set near the earlier prices, under about 0.14 DARWIN (139,673,600,000,000,000 base units), drops every new-curve quote after the switch, and uploads then fail for lack of quotes: raise it before the switch.

Old clients

v0.3.11+ds.4 and older clients check quotes against the earlier curve only, so they refuse every new-curve quote and can't store through ds.20 nodes after the switch. Dan accepted this: those clients are cut off.

Library users: ant-protocol from the same tag

As before, a project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.5" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.5" }

ant_protocol::payment::calculate_price is the new curve. ant_protocol::payment::pricing adds calculate_price_legacy, price_band_legacy, price_in_band_legacy and price_in_accepted_band; price_in_band keeps its meaning, the new curve's band only. The vendored pricing.rs, payment/mod.rs and VENDORED.md are to be the same files as darwin-node 0.23.0+ds.20's: before this release is tagged, a diff of the three files across the two repositories is empty.

Releases

  • This release is published only after the tagged commit's Test run and its Build run on main both passed.
  • This client first. Move services to it before ds.20 nodes reach their switch, at 2026-10-29T00:00:00Z.
  • Then darwin-node 0.23.0+ds.20, its own release, which quotes the earlier curve before its PRICE_SWITCH_AT (1793232000, 2026-10-29T00:00:00Z) and the new curve from then on, and accepts both.
  • Going back to v0.3.11+ds.4, a re-pin of both lines' tags under the usual pause, works only before the switch: after it, v0.3.11+ds.4 refuses the quotes of ds.20 nodes.

darwin v0.3.11+ds.4

Choose a tag to compare

@github-actions github-actions released this 28 Oct 05:23

The darwin client v0.3.11+ds.4 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from 5d9ab3f by the Build workflow (run 40636062434), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release is the pricing switch: the client settles at version 2 and pays each record by its size. A record's paid quote receives 3 × the median quote × the weight of the record's size class, rounded up, in place of 3 × the median quote for every record. A full 4 MiB chunk weighs exactly 1 and pays what it paid before; a smaller record pays less. How payments are made, read back and recovered doesn't change: per chunk, through the per-quote vault, with paid quotes read back, occupied quotes quoted and paid again, the price ceiling, payment recovery and envelopes by default, as in v0.3.11+ds.3.

This release was tested against darwin-node 0.23.0+ds.18 (81dbac7, node release 2, the revision its tests pin), which prices records by size. vendor/ant-protocol is that release's copy, the same tree.

Paying by record size

  • The weight of a record is the vendored pricing::record_weight of its data type and the size class of its bytes: w = 1/4 + 3/4 × (class top ÷ 4 MiB), held as an exact fraction. The size classes have power-of-two tops:

    Class (record size up to) Weight, in 1/1,024 of a full chunk Share of a full chunk's amount
    16 KiB 259 25.3%
    32 KiB 262 25.6%
    64 KiB 268 26.2%
    128 KiB 280 27.3%
    256 KiB 304 29.7%
    512 KiB 352 34.4%
    1 MiB 448 43.8%
    2 MiB 640 62.5%
    4 MiB (a full chunk) 1,024 100%

    A record pays for the top of its class, so never less than in proportion to its size. A file under about 12.6 MB is three chunks of a third of its size, so the class edges people meet as file sizes are about 48 KB, 96 KB, 192 KB, 384 KB, 768 KB, 1.5 MB, 3 MB, 6 MB and 12 MB.

  • What pays by class. chunk_put, the upload engine (files, data maps and envelopes) and pointer_put pay by the record's class. A pointer weighs as a record of its wire length, 5,303 bytes, whatever size a request declares: the 16 KiB class. pay_for_storage, which is given a size and no bytes, pays a chunk at a full chunk's amount, which every node accepts, and quotes and plans a pointer at its wire length.

  • The cost estimate (darwin file cost) prices each chunk paid on its own by its class. Merkle payments stay refused on this network; the estimate prices merkle leaves at a full chunk's amount.

  • The price ceiling (--max-chunk-price) still compares a quote's price, unchanged: a record costs at most 3 × the ceiling × its weight.

  • Occupied quotes are handled as before: a record whose vault record isn't the client's payment (another rewards address, or less than the amount planned for it) is quoted again and paid again, up to 3 times.

Settlement version 2

  • Every quote request declares settlement version 2 (CURRENT_SETTLEMENT_VERSION; MIN_SUPPORTED_SETTLEMENT_VERSION stays 1). A node at node release 2 quotes it at its weight total, the weights of its committed records added up; a node that predates release 2 answers StorerUpdateRequired and is skipped before anything is paid.
  • The unversioned quote retry is gone, on the single-node and merkle paths: a node asked without a version quotes as for version 1 and would refuse the weighted payment made on that quote. A node that can't decode a versioned request is a peer that didn't quote.

The band check

  • A node of release 2 quotes calculate_price(m), where m is its weight total, between floor(259/1,024 × K) and K for K committed records. So a quote's price is checked against a band, from calculate_price of the smallest weight's share of its committed key count to calculate_price of the count, in place of the exact price calculate_price(K). The check is the same in native, merkle and browser quote checks. Every other check of a quote (its signature, its commitment and sidecar, its key cap) is unchanged.
  • A node of full chunks has m = K and quotes exactly as before.

Browsers

  • The browser core's own uploads quote with chunk_protocol frames at BROWSER_PROTOCOL_VERSION whose QuoteRequestV2 declares settlement version 2, so a node that prices by size quotes them by weight, and a node that predates it answers StorerUpdateRequired, which the core skips. They pay each record by its class.
  • A size-priced quote_chunk (the N+1 quote). BROWSER_QUOTE_PROTOCOL_VERSION, one above BROWSER_PROTOCOL_VERSION, is used for a quote_chunk alone, and sent only to a node that advertises size_priced_quote in hello. Hello and reads stay at BROWSER_PROTOCOL_VERSION, so older nodes still serve reads. A quote_chunk at the next version that ends in a closed channel or an invalid_request answer (how darwin-node 0.23.0+ds.17 and older refuse a version they can't parse) fails as unsupported_version, with no quote, and the node is skipped, as a native client skips StorerUpdateRequired.
  • The fallback. verifyStorageQuote takes an optional chunk length. Pass it only for a quote obtained by a size-priced quote_chunk: the amount is then the weighted amount for a chunk of that length. Without it, the amount is a full chunk's, which every node accepts, so a page that pays on any quote not obtained that way pays the full amount.

Old clients and saved uploads

  • Old clients keep working at today's price. A client that declares settlement version 1, or none, is quoted a full chunk's price and pays the full amount, which release-2 nodes always accept.
  • Saved uploads carry across the switch. An upload checkpoint saved by an earlier release restores in this one: each saved plan is reused if its amounts equal the full amount or the weighted amount for the class of its own size, as from v0.3.11+ds.2, and a plan named by a pending payment attempt is settled exactly as recorded, from its journal and the chain. A test pins it: a checkpoint saved before the switch, with a journaled payment and a plan under 4 MiB, reconciles in this release with no new transaction.

Library users: ant-protocol from the same tag

As in v0.3.11+ds.3, a project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.4" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.4" }

SingleNodeQuotePayment::from_quotes and the browser's select_storage_quote and verify_storage_quote take the record's weight, a pricing::RecordWeight (RecordWeight::FULL pays a full chunk's amount), and JavaScript's verifyStorageQuote takes the optional chunk length above. With the devnet feature, also repeat the webrtc patch from darwin-node at the rev in the tag's Cargo.toml.

Releases

  • This release is published only after the tagged commit's Test run (the end-to-end and browser tests, against darwin-node at its pinned revision) and its Build run on main both passed.
  • Nodes first. A node older than release 2 is skipped for quotes, so this release stores only through release-2 nodes. Move to it once the nodes a service uploads through run node release 2.
  • Going back. A revert is a re-pin to v0.3.11+ds.3, under the same pause used to move to this release: a service that takes ant-core by tag pauses its uploads, waits until nothing is unfinished, sets both lines' tags back, and resumes. v0.3.11+ds.3 settles at version 1 and pays the full amount, which release-2 nodes accept, and release-2 nodes keep accepting proofs paid at the weighted amount.
  • Uploads interrupted under this release resume under v0.3.11+ds.3, whose restore accepts both amounts: a checkpoint saved by this release, with a journaled payment, restores there and reconciles with no new transaction. Where you can, finish an interrupted upload with this release before going back. Never delete a checkpoint that holds a pending payment attempt. A client older than v0.3.11+ds.2 stops with an error on a checkpoint written by v0.3.11+ds.2 or later, instead of paying again.

darwin v0.3.11+ds.3

Choose a tag to compare

@github-actions github-actions released this 28 Oct 02:07

The darwin client v0.3.11+ds.3 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from 779769c by the Build workflow (run 40617488680), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release changes how payments are read, not how they are made. Every reader of payments in this client reads two vaults: the per-quote payment vault, as before, and the root vault, which records each wave of payments under one key. Nothing in this client pays the root vault. Prices and quotes don't change: uploads are still planned and paid at the full amount, through the per-quote vault, and the settlement version stays 1.

This release was tested against darwin-node 0.23.0+ds.17 (0deb787, the revision its tests pin). It reads the chain only, and asks nothing new of nodes.

Both vaults are read

  • The vault set. On the bundled network, readers take payments from the per-quote vault and from the root vault, DarwinRootVault, at 0xf6675722f443132b045857546E57e35360Bb03b9 on Robinhood Chain (root_vault_address in ant-core/resources/payment_network.toml). The per-quote vault stays the network's identity. A reader on any other network reads the per-quote vault alone, as before.
  • Who reads them. The listings producer, darwin file verify, listing lookups and envelope payment records all accept a payment from either vault. Each payment is checked by the vault whose log it is. A payment log from a contract outside the set is never a payment.
  • Root waves are checked by their tree. A root vault's payment counts only where its transaction's receipt shows the whole wave: its payments, in quote-hash order; the vault's RootPaid log naming the key the wave's tree gives and the wave's total; and one token transfer from the payer to each of the wave's payees, of the sum of its payments. A receipt that doesn't show the wave whole reads as unpaid.
  • A quote paid again. A root vault can record a quote again in a later wave, so a reader with one searches again when none of a quote's kept payments is the one a listing shows, and keeps what it finds beside them. A reader of the per-quote vault alone doesn't search again, as before.

One log query per vault, in one batch

  • One address per query. Each vault is asked in its own eth_getLogs, naming its one address, and both queries are sent in one batch and merged in chain order (block, then log index). Robinhood Chain's RPC allows 10,000,000 blocks per log query when each filter position holds one value, but only 100,000 when any position holds a list, so one query naming both addresses would fail a quote's search on its first query.
  • The log-query budget. A search for a quote's payment makes at most 64 rounds of log queries. A round is one query for a reader of the per-quote vault alone, and two, one per vault, with the root vault: at most 128 queries. A quote paid nowhere costs a round or two each time it is decided again (two to four queries with the root vault), not a new search.
  • Kept searches. How far a search that found nothing went through final blocks is kept, so a later search goes on from there. With the root vault, it is kept per listing log index, so a later search never skips a payment at that index; a reader of the per-quote vault alone keeps a search only when it saw no payment at all, and shares it across indexes, as before.

Kept receipts are read again once after upgrading

  • Kept receipts now hold each log's address and data, with the root vault's token's transfers, and are keyed by the vaults read.
  • The first start after upgrading of a reader with the root vault (every reader on the bundled network) drops the receipts and quote payments kept for the per-quote vault alone, since they lack the root vault's logs. Each is read again once, with both vaults' logs, as listings need it: a one-time burst of chain reads, paced like any other, after which they are kept again. Blocks already kept stay.

Also in this release

  • The vendored darwin-listings is darwin-listings 0.4.0 (vendor/ant-listings; the commit it was vendored at is in its VENDORED.md). It reads a set of vaults and a root vault's waves, checked against the root vault's own test vectors. The listing format, the upload proof format and the listings protocol's messages are unchanged.
  • A census test (every_payment_reader_reads_the_vault_set) lists every reader of payments, so a new one is reviewed before it ships.
  • Other readers. Readers that aren't updated (older darwin-listings, older CLIs, other indexers) read a payment through the root vault as unpaid: a listing or upload proof paid that way fails their check until they update.

Library users: ant-protocol from the same tag

As in v0.3.11+ds.2, a project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.3" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.3" }

ListingChain::new reads the bundled network's root vault on its own, and ListingChain::with_root_vault adds one elsewhere. The functions of ant_core::ant_listings that took one vault's address (find_payment, verify_listing, verify_listing_receipts, check_offline, check_proof) take a &VaultSet: VaultSet::v2_only(vault) reads as before, and VaultSet::with_root also reads a root vault. With the devnet feature, also repeat the webrtc patch from darwin-node at the rev in the tag's Cargo.toml.

Releases

  • This release is published only after the tagged commit's Test run (the end-to-end and browser tests, against darwin-node at its pinned revision) and its Build run on main both passed.
  • Going back. A revert is a re-pin to v0.3.11+ds.2, under the same pause used to move to this release: a service that takes ant-core by tag pauses, sets both lines' tags back, and resumes. v0.3.11+ds.2 reads the per-quote vault alone, so it reads a payment through the root vault as unpaid.
  • Nothing to finish first. This release changes no upload checkpoint, payment or price, so uploads interrupted under it resume under v0.3.11+ds.2 as they would under this release.

darwin v0.3.11+ds.2

Choose a tag to compare

@github-actions github-actions released this 27 Oct 21:44

The darwin client v0.3.11+ds.2 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from 86570fa by the Build workflow (run 40586891290), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This release changes how uploads pay and resume. Prices and quotes don't change: uploads are still planned and paid at the full amount, and the settlement version stays 1. What changes is how a payment is recorded, shared and reused, so that an interrupted upload pays for each record once.

This release was tested against darwin-node 0.23.0+ds.17, whose full nodes decline to quote a pointer they don't hold; its pointer-write check counts the nodes that quote. Against older nodes the check changes nothing.

Upload checkpoints: a format of their own

  • The format. A checkpoint keeps its path and name, and now starts with DSCK and a format byte, then its body in msgpack. An earlier client reads the whole file as msgpack, fails, and stops with an error instead of paying again.
  • Paid proofs are kept beside a native checkpoint, in <stem>.proofs, appended once each instead of rewritten at every step. A record cut off at the end of the file, as after a crash, is left out and written anew. A proof file that is missing, or shorter than its checkpoint records, refuses the checkpoint.
  • Older checkpoints still load. A native msgpack checkpoint is migrated when it is opened: its proofs are written to a new proof file first, and the old file is replaced only once the new one is complete.
  • Browsers. The browser core still hands on_checkpoint a string, now the checkpoint's bytes as base64: store it as text, as it is, and pass it back to resume. It also takes the bytes as a Uint8Array or as hex, and reads the JSON text earlier browser clients saved.
  • Restoring a saved plan. A plan is reused only if its amounts equal one of exactly two recomputations from its signed quotes: the full amount, or the amount weighted for the size of its own data. A plan at neither is dropped and quoted again. A plan named by a pending payment attempt is never dropped or quoted again: it is settled exactly as recorded, from its journal and the chain. When saved plans are checked, the whole checkpoint is refused only for bad signatures or identities, or for a pending attempt that can't be checked (a missing or damaged proof file refuses it too, as above); the error then names the file and says not to delete it. One bad unpaid plan doesn't discard paid proofs.
  • For servers: upload_checkpoint_files lists a checkpoint's files, for removing what ended uploads leave behind.

Shared payment waves

  • One transaction for small payments. Payments of fewer than 64 records, from uploads paying with the client's own wallet, join that wallet's open wave, which pays every member's quotes in one transaction. A wallet still has one transaction in flight at a time.
  • When a wave is sent: once it is full, once it holds every upload the client has in progress, or 500 ms after it opened.
  • No record is quoted twice. Every file in a wave records the wave's journal before anything is signed for it, and no file in the wave is quoted again until that transaction is confirmed, reverted or replaced. A resume reconciles the wave's transaction before any file pays again.
  • Each file's own record. A file's payment, proofs and envelope payment record show only its own quotes and amount, and its share of the gas.
  • For higher rates, use one wallet per worker. Shared waves help one wallet's many small uploads; separate workers paying from separate wallets don't share waves.

Paid proofs are kept until stored

  • No age limit on paid proofs. A paid single-node proof is kept until its record is stored, as darwin-node accepts an old paid quote. Quotes not yet paid keep their age limit, and merkle proofs their one week.
  • Reused on resume while the node that was paid is among the 20 nearest storers in the fresh lookup the resume already does. Other storers are tried first.
  • Paid again only when needed: when that lookup puts the paid node outside the 20, or the payment is missing or reverted on chain, or every storer tried refuses the old proof. So an upload never stalls on an old proof.
  • At most once. A record is paid again this way at most once across all runs, marked in its checkpoint before the new payment is sent. A proof this run paid, or settled from its journal, is never paid again. While the payment's vault record can't be read, or where the client reads no such record at all (a browser build, or a client with no payment network), the proof is kept and the record fails with a message saying why. A browser that held the paid node off, or couldn't reach it, keeps the proof and leaves the decision to the storers.
  • Cleaning up. Proofs are deleted with their upload. Checkpoints and cached receipts that nothing has touched for 30 days, and that hold no pending payment, are removed.

Pointer writes: nothing paid unless five of the seven can take the write

  • Native and browser clients count the nodes that quoted a pointer's state before paying, and stop before anything is paid when fewer than 5 of the 7 in its close group did. 5 of 7 is the pointer's write quorum. With darwin-node 0.23.0+ds.17, a full node declines to quote a pointer it doesn't hold, so the count is of the nodes that can store the write.

The listings reader asks Robinhood's RPC first for logs

  • On the bundled network, the listings reader's log queries go to Robinhood Chain's RPC first and ordofi's last, which answered payment reads busy or too late in October 2026. Other calls keep PublicNode first.
  • A "busy" answer (JSON-RPC error -32005) to a log query from ordofi, which gives it to a query over a wider span than it serves, fails that query alone and holds nothing off; the query goes on to the next provider. From any other provider it holds that provider off, as before.

Also in this release

  • darwin listings serve's census keeps each node's browser (WebRTC Direct) addresses, read from the node's signed address record.
  • The vendored ant-protocol holds darwin-node's functions for pricing by record size. Nothing in this release plans or pays with them: the checkpoint's restore uses them only to recognise a plan saved at a record's weighted amount.
  • THIRD-PARTY-NOTICES.txt lists ant-listings, the vendored copy of darwin-listings, with its licence and source again.

Library users: ant-protocol from the same tag

As in v0.3.11+ds.1, a project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.2" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.2" }

With the devnet feature, also repeat the webrtc patch from darwin-node at the rev in the tag's Cargo.toml.

Releases

  • This release is published only after the tagged commit's Test run (the end-to-end and browser tests, against darwin-node at its pinned revision) and its Build run on main both passed.
  • Going back. A revert is a re-pin to v0.3.11+ds.1, under the same pause used to move to this release: a service that takes ant-core by tag pauses its uploads, sets both lines' tags back, and resumes.
  • Finish interrupted uploads first. If you used the darwin CLI for an upload that was interrupted, finish it with this release before downgrading. An earlier client stops with an error on this release's checkpoints rather than paying again.
  • Checkpoints. A checkpoint that holds a pending payment attempt is never deleted, and a journaled payment attempt is never quoted again.

darwin v0.3.11+ds.1

Choose a tag to compare

@github-actions github-actions released this 27 Oct 05:11

The darwin client v0.3.11+ds.1 for Linux x86_64 (Ubuntu 24.04 or newer).

Built from 21bc5dc by the Build workflow (run 40451761963), and published once that run and the commit's Test run had passed.
Check the archive with: sha256sum -c SHA256SUMS.txt

The archive holds darwin, LICENSE-MIT and LICENSE-APACHE, plus THIRD-PARTY-NOTICES.txt and RUST-STD-COPYRIGHT.html with the licences and copyright notices of the third-party code in the binary and of the tools used to build it.

What changes

This is the first published release of the darwin client and of the ant-core library built from this repository. Prices, quotes and payments don't change, and the settlement version stays 1. Library users can take this release by its tag, with ant-protocol patched from the same tag (see "Library users" below).

This release was tested against darwin-node 0.23.0+ds.16, the first node release that refuses a browser session quickly when it is busy. It works with older nodes too: against them, browsers fall back to the full timeout and cooldown described below.

Browsers: three random seeds, and busy nodes held off for 15 seconds

  • Three seeds, not all of them. A browser keeps sessions with three seeds, taken in a random order. A seed whose dial fails or is declined is replaced by the next one. Three more are taken only when lookups through the first ones fall short, as on a network whose seeds know few of the other nodes. A seed's session stays open until the node closes it for idleness, so a seed that holds a record is read on the session already open.
  • Random order everywhere. The darwin CLI tries its seeds in a random order after the peers it has cached, and so does a library client built from seeds. Client starts spread across every seed instead of landing on the first few listed.
  • A busy node is held off for 15 seconds. The handling follows upstream ant-client #225, without its spreading of reads:
    • a dial that the node answers and declines counts as busy: an association that fails within 3 seconds of its connection checks starting, before they succeed, or one the node closes right after DTLS or during the handshake;
    • a busy node is held off for 15 seconds, and the browser tries the next seed. Every other failed dial keeps the 30-minute cooldown, including a DTLS failure after the connection checks succeeded, which means another node answered at that address;
    • BrowserNetwork::refused_peers() lists the peers held off as busy, with the time left;
    • a read whose possible holders are all busy waits for the first of them to come back, up to three times, then fails with "the nodes that may hold chunk ... are busy; try again shortly". Before, it reported the chunk as missing;
    • a busy node delays a read only while it could be among the record's nine nearest nodes.
  • When every seed is busy, connect() says how many are.

Reads ask fewer seeds

  • Before: every chunk and pointer read asked the nine seeds nearest its address from the start, connected or not.
  • Now: a read asks no seed before its lookup answers, apart from reading any peer already connected early. It then asks the seeds that are still among the nine nearest of everything it knows, or all nine nearest when the lookup found no peer. While the seeds are most of the network, they are most of every read's nearest nodes; as other nodes join, each read asks fewer of them.
  • Pointer reads look up the record's close group first, and ask at least two seeds. pointer_finality's census and read share that lookup.
  • Pointer reads and finality checks with busy peers. A busy peer is left out of a lookup before it is asked, so farther peers can answer in its place. When a busy peer ranks in the close group and too few of the group answered, the result isn't reported as absent, open or at an older state: the read, census or finality check waits out the hold-off and runs again, up to three times, then fails as busy. A census whose every peer holds one final state, while a busy peer in the close group was never asked, reports the state as unconfirmed rather than final. The update and transfer checks before payment count busy peers in the same way. A native client holds no peer off, so its reads behave as before.

Repeat reads: the last holder first, and a chunk cache sized by memory

  • Holders. A client remembers which node served each chunk it read in the last 15 minutes, up to 1,024 chunks. A read of a chunk no longer in the cache asks that node first, and looks the chunk up only once the node answers without it, or hasn't answered within 1 second. The remembered node is only a hint: its answer never makes a chunk missing, and a node that no longer serves the chunk is forgotten. Reads with download diagnostics on work as before.
  • The default chunk cache holds at most 1,024 chunks and at most an eighth of the memory free when the client is built, never less than one full chunk (4 MiB) and never more than 256 MiB. It evicts the least recently used chunks, and doesn't cache a chunk larger than its byte limit. It held 1,024 chunks whatever their size before, up to 4 GiB, which a long download on a small machine could fill. ChunkCache::with_byte_budget builds a cache with other limits, and ChunkCache::new a cache limited by chunk count alone, as before.

The update message is darwin's own

  • When nodes refuse this client's settlement version, the error now says which version the client settles under and the minimum the network requires, that no quote was issued and nothing was charged, and that a new darwin is built from https://github.com/Project-DSpace/darwin-client (see its README). Before, it repeated the node's wording, which pointed to ant update.
  • In a browser build, whose code the page serves, the message says to reload the page.

saorsa-transport 0.38.2

  • The lock moves to saorsa-transport 0.38.2, and ant-core now requires at least 0.38.2. 0.38.1 kept every past QUIC connection's finished reader task allocated for as long as the endpoint lived; 0.38.2 reaps finished readers.
  • A test (lock_pins) holds the committed lock to one saorsa-transport at 0.38.2 or later.

Library users: ant-protocol from the same tag

A project that uses ant-core takes both lines below, with the same tag:

[dependencies]
ant-core = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.1" }

[patch.crates-io]
ant-protocol = { git = "https://github.com/Project-DSpace/darwin-client", tag = "v0.3.11+ds.1" }
  • The patch puts this repository's vendor/ant-protocol, Autonomi's protocol crate with this network's price multiplier, in place of the crates.io release. Cargo applies a [patch] only in the top-level workspace, so taking ant-core alone isn't enough.
  • ant-core now requires exactly ant-protocol 5.0.0, the vendored copy's version, so a newer crates.io release can't take the patch's place when the lock is made again. Without the patch, ant-core doesn't compile, rather than checking and paying quotes at Autonomi's prices.
  • With the devnet feature, also repeat the repository's other patch, webrtc from darwin-node at the rev in the tag's Cargo.toml: webrtc = { git = "https://github.com/Project-DSpace/darwin-node.git", rev = "<rev>" }. devnet runs darwin-node in process, and darwin-node's patched webrtc cancels a DTLS handshake still in progress when a browser session closes; without the entry, the devnet's nodes are built with crates.io's webrtc 0.14.0. A build without devnet doesn't link webrtc at all, so it needs only the ant-protocol line.

Also in this release

Earlier work on main, now in a published release for the first time:

  • New chunks go to their whole close group (all seven nearest nodes), where library builds from before 15 October sent each chunk to four of the seven.
  • The node manager's own log (darwin-nodes daemon run --log-path PATH) is written as PATH.<UTC time>.log, a new file every 10 MiB, every hour and at every start, and the family is kept within 50 MiB, with closed files older than a month deleted. At or below 1 GiB free on the log's partition it deletes its closed files and writes only warnings and errors, then says how many lines it left out once space returns. Node logs are never touched.

Releases

  • This release is published only after the tagged commit's Test run (the end-to-end and browser tests, against darwin-node at its pinned revision) and its Build run on main both passed. A client release is tested against a darwin-node revision on darwin-node's main, at or after node release 0.23.0+ds.16.
  • Going back. A revert means installing the previous client. If you used the darwin CLI for an upload that was interrupted, finish it with this release before downgrading.
  • Checkpoints. A checkpoint that holds a pending payment attempt is never deleted, and a journaled payment attempt is never quoted again.