Skip to content

Storage node v0.23.0+ds.20

Latest

Choose a tag to compare

@github-actions github-actions released this 28 Oct 11:38
Immutable release. Only release title and notes can be modified.

Storage node v0.23.0+ds.20 for Linux x86_64 (Ubuntu 24.04 or newer).

Running nodes install it by themselves within the staged-rollout window.
The archive is signed with ML-DSA-65; check it with:
ant-keygen verify --key release-signing-key.pub --input ant-node-cli-linux-x64.tar.gz --signature ant-node-cli-linux-x64.tar.gz.sig

The archive also contains 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

ds.20 brings two changes. It changes the price of storage, and it adds a third way for a client to pay for records: root payments, which pay for a whole wave of records in one transaction (settlement version 3). Dan, 2026-10-28: "no i want to combine them" (decisions log).

The price change sends no new kind of message, field or reply: what changes is the price curve nodes quote, and which prices storers accept. Dan, 2026-10-28: "make chunk price 100 darwin = 1gb" and "update to D = 2000". Root payments are new: a client released before ds.20 is quoted and paid as before on every node, apart from the new curve's prices once nodes switch (see "Who is cut off"). Merkle (batch) payments are still refused.

The price of storage

The new curve

  • The formula is upstream's, price = BASELINE + K × (n ÷ D)², with n the records a node has committed to: BASELINE = 0.1396736 DARWIN, K = 9 × BASELINE (1.2570624 DARWIN), D = 2,000 records.
  • Where the numbers come from. 100 DARWIN per 10⁹ 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. K stays 9 × BASELINE as upstream, and with D = 2,000 the price reaches 10 × at 2,000 records instead of 6,000.
  • What a GB of full chunks costs: exactly 100 DARWIN on empty nodes, 1,000 DARWIN at 2,000 records, 8,200 DARWIN at 6,000 records. Smaller records pay by their size class as in ds.19.

The switch

  • Nodes quote the new curve from 2026-10-29T00:00:00Z (unix 1793232000; a placeholder, see "Before tagging"), by the node's own clock when it makes the quote. Before then a ds.20 node quotes the previous curve, Autonomi's × 32 (0.125 DARWIN at an empty node, D = 6,000), exactly as ds.19 does.
  • ds.20 storers accept a quote on either curve, each checked with today's rules: the price within the band its committed key count allows, the size-class weights, and the 3 × median × weight requirement. A payment whose quotes mix the two curves, as around the switch, is accepted.
  • The revenue floor (off unless ANT_PRICE_FLOOR_ENFORCE is set) prices its reference on the curve the paid quote is on, so a payment on the previous curve is never held to the new one.
  • The floor's margins move on the new curve. The new curve rises faster, so at the default 65% tolerance three of fifteen neighbours overstating their record counts make a node that enforces the floor refuse an honest payment at the bottom of the true median's band (on the previous curve it took four). In shadow mode, the default, such a payment is only logged with below_floor=true. Four understating neighbours no longer disarm the floor. Whether the default tolerance is re-derived for the new curve is open for Dan.

Who is cut off by the price change

  • Nodes on ds.19 and older refuse quotes on the new curve, and with them every payment that carries one. Every node needs ds.20 before the switch; nodes install it by themselves.
  • Clients v0.3.11+ds.4 and older refuse new-curve quotes, so they can't pay once nodes switch. Dan accepted this. v0.3.11+ds.5 accepts both curves.

Root payments

How a root payment works

  • One transaction per wave. A client can pay a wave of up to 512 records in one transaction through the root vault, 0xf6675722f443132b045857546E57e35360Bb03b9 on Robinhood Chain. The vault records the whole wave under one key, built from a tree whose leaves each bind a paid quote's hash, its payee's whole address and the amount paid to it. A wave of many records to one payee costs far less gas per record than paying each record alone.
  • Priced as any quote. A root proof's quotes are the same quotes a per-record proof carries, on the same curve: each leaf is the size-class amount of its paid quote, so the new curve and the acceptance of both curves apply to root payments exactly as to per-record ones, and so does the revenue floor, on the paid quote's curve.
  • The proof. A root proof (tag 0x03) carries the same quotes as a per-record proof, plus the paid leaf's amount, its place in the wave, the wave's size and the path of hashes up to the wave's key. The node runs every check a per-record proof gets, rebuilds the leaf from the signed median quote, hashes it up the path, and reads the vault's record under the key, once more a moment later if it reads empty. A malformed or underpaid root proof is refused before anything is read. The amount is held to the same size-weighted rule as a per-record payment.
  • Each proof in its own vault. Root proofs settle in the root vault's records alone, per-record proofs in the payment vault's alone. Merkle proofs are still refused first, before anything else is read.
  • Built in for the production network. A node paid in DARWIN through its payment vault knows the root vault without any setting, so a node whose binary was upgraded alone knows it too. A custom network gives its own with --evm-root-vault (ANT_EVM_ROOT_VAULT); with none, root proofs are refused. A production node whose setting names a different root vault warns at start. The node's payment vault, which the image and browsers check, is unchanged.

Settlement version 3

  • Version 3 means "root proofs accepted". A node that knows a root vault quotes a version-3 request as it quotes a version-2 one, priced by size the same way. A node with no root vault, like any node before ds.20, answers a version-3 request that it must be updated, naming version 2, before quoting, so an up-to-date client never pays through the root vault to a node that can't check the payment.
  • Nothing older is turned away. The lowest version a node takes stays 1.

Root-paid records between nodes

  • Slim root proofs. A root proof is forwarded to other nodes only in its slim form, as a per-record proof is since ds.19: the quotes at the median price (the paid one is always among them), the transaction hashes, and the leaf whole, with no commitment sidecars.
  • A refusal at payment verification is excused. A node that stores a root-paid record leaves out of the record's possession check a close-group peer that refuses its offer at payment verification, as a node that can't check a root proof does, whether or not the proof holds a quote from that peer. Neighbour sync places the record with it later. A peer that refuses for any other reason (out of range, out of space, a failed write) is checked as before, as a refusal of a per-record offer is, and the peer whose quote was paid is always checked: a node paid for a record it then doesn't take is charged. A peer other than the paid one could word any refusal as a payment-verification one to skip that one check, since the reason is its own word; it is still checked at any later possession check.
  • Refusals of root-paid offers are answered. Offers go between nodes one-way. ds.20 answers a one-way offer only to refuse a root-paid one, so the sender can excuse it; every other answer to a one-way offer is still not sent, as in ds.19. A ds.19 node answers no one-way offer, so a ds.19 peer that refuses a root-paid offer stays in its possession check.
  • A "full" peer. The possession check's charge of a full peer that was paid for the record reads a root proof's one paid quote.

Listings

  • Both vaults. The listing service reads a listed record's payment from a per-record or a root proof, asking each vault for its payment logs in its own query, both in one batch, or each alone from an RPC that takes no batches. darwin-listings is now 0.4.0.

New counters

  • The traffic lines count quote requests by settlement version, settled proofs by tag, and the root vault's record read as a read of its own.

Before tagging

  • Set PRICE_SWITCH_AT from the tag time (decisions log, 2026-10-28: "PRICE_SWITCH_AT must fall after the combined release is on our nodes, so it is set when ds.20 is tagged"): at least the release's publication + the staged rollout (24 h by default) + one upgrade check (1 h) + the canary period and approval gate, and after darwin-client v0.3.11+ds.5 is deployed on the gateway and the CLIs. Change the constant in src/payment/pricing.rs, its pin the_switch_is_the_tagged_time, README.md and these notes together.

Updating the image (Docker)

ds.20 changes nothing in the image. Nodes install ds.20 by themselves, with everything above, whether or not you pull.