feat: add file cost estimation command and public API#44
Conversation
Add `ant file cost <path>` to estimate upload cost without uploading. Encrypts the file locally to determine chunk count, requests a single quote from the network for a representative chunk, and extrapolates the total storage cost. No wallet required. New public API: `Client::estimate_upload_cost(path, mode)` returns `UploadCostEstimate` with file size, chunk count, storage cost in atto, estimated gas in wei, and payment mode. Gas estimation uses a conservative heuristic based on chunk count and payment mode (merkle vs single). Storage cost is the median quoted price multiplied by chunk count. Supports --json for structured output and --merkle/--no-merkle to override payment mode selection.
OverallSolid feature. API is clean (option-style args, serializable result, no-wallet path, progress hook), the cost extrapolation correctly mirrors IssuesP1 — AlreadyStored path returns a misleading "free" estimate
Two cleaner options:
P1 — Single-mode gas heuristic is likely off by a large factorlet waves = chunk_count.div_ceil(UPLOAD_WAVE_SIZE);
(waves as u128) * 150_000 * 1_000_000_000
Either bump the per-wave constant, measure it once against an actual upload, or clearly label single-mode gas as "minimum possible" rather than "estimated." P2 — polish
Nits
Nothing security-sensitive; no wallet is touched, estimate is advisory, spill cleanup runs on Drop. The P1 items are the ones I'd want fixed before merge; the rest can go in a follow-up. |
P1 fixes from Nic's review on #44: - Drop the AlreadyStored -> "free" best-effort branch. A majority confirming the first chunk is stored says nothing about the other 99% of chunks, so returning a zero-cost estimate was misleading. Now surfaces a typed InvalidData error so callers can retry instead of trusting the bogus cost. - Rework single-mode gas heuristic. batch_pay flattens every chunk's close group quotes into one pay_for_quotes call, so gas scales with the number of quote entries in the wave (chunks x recipients/chunk), not with the number of waves. 150k/wave was off by 5-10x on full waves; replace with 75k base + 25k per entry, summed across waves. Bump merkle budget to 500k/sub-batch to reflect tree verification + pool commitment. P2 polish: - Drop the redundant compute_address re-hash on the first chunk; the spill address is already the content address. - Replace `chunk_count as u64` with a checked conversion to match the rest of the file. - Wire the progress hook through handle_file_cost so large-file encryption emits Encrypting / Encrypted events instead of a static spinner, reusing drive_upload_progress. All 4 e2e_cost_estimate tests still pass on a local devnet.
P1 — AlreadyStored branch now samples up to ESTIMATE_SAMPLE_CAP chunk addresses instead of trusting a single probe. Only returns zero-cost when every address in the file is confirmed stored; otherwise returns a new typed Error::CostEstimationInconclusive so callers can handle it cleanly. The CLI renders this case with a helpful retry-suggestion message. P1 — Replace the per-wave gas heuristic with named constants: GAS_PER_WAVE_TX = 1_500_000 gas (Arbitrum pay_for_quotes with 64 entries) GAS_PER_MERKLE_TX = 500_000 gas ARBITRUM_GAS_PRICE_WEI = 100_000_000 (0.1 gwei baseline) Each constant carries a comment explaining where the number comes from and that it is advisory, not a live oracle query. No change to the chunk-count conversion or progress plumbing (already done in the previous review commit).
|
Hey @nirh-autonomi — pushed a follow-up. Mapping to the review items: P1 — AlreadyStored "free" estimate → fixed. Instead of trusting a single probe, P1 — Single-mode gas heuristic → replaced the 150k/wave magic number with three named constants, each with a doc comment explaining where the number comes from:
Gas is now P2 — Redundant P2 — P2 — CLI drops progress hook → already wired in af1e894: P2 — PR description example mismatch → fixed in the PR body. With the new constants a 13-chunk file is one wave: No CHANGELOG in the repo, so nothing to update there. Let me know if you want the sample cap bumped or the gas price made configurable before we merge. |
Nic-dorman
left a comment
There was a problem hiding this comment.
macOS CI failure is due to failing to get enough quotes, not related to the PR
Summary
Adds
ant file cost <path>to estimate upload cost without uploading. Encrypts the file locally to determine chunk count, samples one or more network quotes, and extrapolates the total. No wallet required.New public API:
Client::estimate_upload_cost(path, mode, progress)returnsUploadCostEstimate.Example output
JSON (
--json):{"file_size":52428800,"chunk_count":13,"storage_cost_atto":"49556250000000000","estimated_gas_cost_wei":"150000000000000","payment_mode":"single"}13 chunks fits in one 64-chunk wave; gas = 1 wave ×
GAS_PER_WAVE_TX(1.5M gas) ×ARBITRUM_GAS_PRICE_WEI(0.1 gwei) = 1.5 × 10^14 wei = 0.000150 ETH. Human output and JSON agree on the same wei value.Accuracy
Tested against actual uploads on a local devnet. Storage cost estimates match actual costs exactly:
Gas is an advisory heuristic based on per-transaction budgets (see constants below), not a live gas-oracle query. Treat it as an order-of-magnitude figure.
Changes
ant-core:
Client::estimate_upload_cost(path, mode, progress)with encryption progress supportUploadCostEstimatestruct (Serialize/Deserialize, JSON-safeStringamounts)PaymentModegainsSerialize/Deserialize+#[serde(rename_all = "snake_case")]SingleNodePayment::from_quotesGAS_PER_WAVE_TX = 1_500_000(onepay_for_quotescall on Arbitrum, up to 64 entries)GAS_PER_MERKLE_TX = 500_000(one merkle sub-batch tx)ARBITRUM_GAS_PRICE_WEI = 100_000_000(0.1 gwei baseline)ESTIMATE_SAMPLE_CAP = 5chunk addresses before deciding. Returns the new typedError::CostEstimationInconclusivewhen every sample reports stored; only returns a zero-cost estimate when every address in the file is sampled and all are stored.ant-cli:
ant file cost <path> [--merkle | --no-merkle] [--json]drive_upload_progressso the encryption phase shows chunk progress, not a static spinnerTests:
CostEstimationInconclusiveerror variantDesign decisions
progressparameter accepts anUploadEventsender for encryption progress on large files.Test plan
cargo clippy --all-targets --all-features -- -D warningscargo fmt --all -- --checkcargo test -p ant-core --libpassestest_estimate_matches_actual_cost(4 file sizes, 15% tolerance)test_estimate_works_without_wallettest_estimate_payment_modetest_estimate_rejects_tiny_files