Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

antd-ruby

Ruby SDK for the antd daemon — the gateway to the Autonomi decentralized network.

Installation

Add to your Gemfile:

gem "antd"

Or install directly:

gem install antd

Compatibility

This gem talks to a running antd daemon; it does not join the network itself. Ruby 3.1+. Tested against antd 0.12.x. The REST client has no runtime dependencies; the gRPC transport (Antd::GrpcClient) needs the grpc gem, which is deliberately not a runtime dependency of this gem — add gem "grpc" yourself to use it.

Quick Start

require "antd"

client = Antd::Client.new

# Check daemon health
health = client.health
puts "OK: #{health.ok}, Network: #{health.network}"

# Store data
result = client.data_put_public("Hello, Autonomi!")
puts "Stored at #{result.address} (chunks: #{result.chunks_stored})"

# Retrieve data
data = client.data_get_public(result.address)
puts "Retrieved: #{data}"

gRPC Transport

The SDK includes an Antd::GrpcClient class that provides the same methods as the REST Antd::Client, but communicates over gRPC.

Setup

Install the gRPC gem (listed as an optional development dependency):

gem install grpc grpc-tools

Generate the Ruby protobuf/gRPC stubs from the proto definitions:

grpc_tools_ruby_protoc \
  -I../../antd/proto \
  --ruby_out=lib --grpc_out=lib \
  antd/v1/common.proto antd/v1/health.proto antd/v1/data.proto \
  antd/v1/chunks.proto antd/v1/files.proto

The generated files are expected under lib/antd/v1/.

Usage

require "antd"
require "antd/grpc_client"

client = Antd::GrpcClient.new  # defaults to localhost:50051

# Or custom target:
# client = Antd::GrpcClient.new(target: "my-host:50051")

health = client.health
puts "OK: #{health.ok}, Network: #{health.network}"

result = client.data_put_public("Hello via gRPC!")
puts "Stored at #{result.address}"

data = client.data_get_public(result.address)
puts "Retrieved: #{data}"

The GrpcClient raises the same Antd::AntdError hierarchy as the REST client, translating gRPC status codes to the appropriate error subclass (an ABORTED whose status details — GRPC::BadStatus#details, the text the daemon sent — start with Partial upload: becomes Antd::PartialUploadError, with the chunk counts, retryable and retention_known parsed from that text — see Partial uploads; any other ABORTED, including one that only mentions Partial upload: later in its text, stays a generic Antd::AntdError).

Note: Wallet operations (address, balance, approve) and payment_mode are available via REST only.

Prerequisites

The antd daemon must be running. Start it with:

ant dev start

Configuration

# Default: http://localhost:8082, 300 second timeout
client = Antd::Client.new

# Custom URL
client = Antd::Client.new(base_url: "http://custom-host:9090")

# Custom timeout (seconds)
client = Antd::Client.new(timeout: 30)

# Both
client = Antd::Client.new(base_url: "http://custom-host:9090", timeout: 30)

API Reference

Health

Method Description
health Check daemon status

Data (Immutable)

Method Description
data_put_public(data, payment_mode: :auto) Store public data — returns DataPutPublicResult (DataMap stored on-network)
data_get_public(address) Retrieve public data by address
data_put(data, payment_mode: :auto) Store encrypted private data — returns DataPutResult (DataMap returned to caller)
data_get(data_map) Retrieve private data using a caller-held DataMap
data_cost(data, payment_mode: :auto) Estimate storage cost — returns UploadCostEstimate with size, chunks, gas, payment mode

Chunks

Method Description
chunk_put(data) Store a raw chunk
chunk_get(address) Retrieve a chunk

Files

Method Description
file_put(path, payment_mode: :auto) Upload a file privately — returns FilePutResult (DataMap returned to caller)
file_get(data_map, dest_path) Download a private file using a caller-held DataMap
file_put_public(path, payment_mode: :auto) Upload a file publicly — returns FilePutPublicResult (DataMap stored on-network)
file_get_public(address, dest_path) Download a public file by address
file_cost(path, is_public, payment_mode: :auto) Estimate upload cost — returns UploadCostEstimate with size, chunks, gas, payment mode

Error Handling

All errors inherit from Antd::AntdError and can be caught by type:

begin
  data = client.data_get_public(address)
rescue Antd::NotFoundError => e
  puts "Data not found on network"
rescue Antd::PaymentError => e
  puts "Insufficient funds"
rescue Antd::AntdError => e
  puts "Error #{e.status_code}: #{e.message}"
end
Error Type HTTP Status When
BadRequestError 400 Invalid parameters
PaymentError 402 Insufficient funds
NotFoundError 404 Resource not found
AlreadyExistsError 409 Resource exists
ForkError 409 Version conflict
TooLargeError 413 Payload too large
InternalError 500 Server error
NetworkError 502 Network unreachable
PartialUploadError 502 (code: "PARTIAL_UPLOAD") Finalize stored some chunks but not all — subclass of NetworkError

Partial uploads

A finalize_upload / finalize_merkle_upload / finalize_chunk_upload call can fail after the external signer has paid: some chunks store, others miss quorum after the daemon's own retries. The daemon reports this as HTTP 502 with code: "PARTIAL_UPLOAD" (gRPC ABORTED), and the SDK raises Antd::PartialUploadError carrying chunks_stored, chunks_failed, total_chunks, retryable and retention_known. The on-chain payment persists and the stored chunks stay on the network; what to do next depends on what the daemon said about the paid attempt:

  • retryable (antd >= 0.14.0; implies retention_known) — the daemon kept the paid attempt under the same upload_id. Call the same finalize method again with the same upload_id and payment artefacts to store the remainder against the same payment: no re-prepare, no second signature, no double payment. Bound the loop — a persistent failure raises this error on every call — so cap the attempts and treat a chunks_failed that stops shrinking as stuck. The retained attempt expires with the daemon's pending-upload TTL.
  • retention_known && !retryable — the daemon confirmed nothing was retained (a merkle finalize with deliberately unpaid batches). Re-prepare the same content: already-stored chunks are skipped, so the retry pays only for the remainder.
  • !retention_known — retention is unknown: the error did not say, in a form the SDK could read, whether the paid attempt was kept. The daemon may still hold it (it records the resume handle before it returns the error). Stop automatic recovery, keep the upload_id and the original payment artefacts (tx hashes, or the merkle winner pool hash), and reconcile before re-preparing or paying again. Never pay again on this signal alone. Daemons older than 0.14.0 never send retryable, so their REST partial uploads read as unknown.

Over REST, retention_known is true only when the body's retryable is a JSON boolean (true or false); absent, null or any other type reads as unknown. Over gRPC there is no structured body, so the counts and flags are parsed from the status text, Partial upload: <stored>/<total> chunks stored, <failed> failed after retries: <reason> (<hint>). retention_known is true only when the text starts with that counts pattern, all three counts parse (each within the daemon's u64 range), and the text ends with one of the daemon's two hints: (paid attempt retained...) sets retryable, and (stored chunks persist; re-prepare the same content...) means the daemon confirmed nothing was retained (daemons older than 0.14.0 write only this one). If the counts cannot be read, all three are 0 and both flags are false. Readable counts with a missing, truncated or unrecognised hint keep the counts, but both flags stay false: retention is unknown, so stop and reconcile rather than re-prepare.

PartialUploadError subclasses NetworkError (the 502 mapping), so existing rescue Antd::NetworkError blocks keep catching it; rescue the subclass first to handle it specifically.

MAX_ATTEMPTS = 5
last_failed = nil
attempt = 0
begin
  attempt += 1
  result = client.finalize_upload(prep.upload_id, tx_hashes)
rescue Antd::PartialUploadError => e
  # Not resumable. With retention_known, nothing was retained: re-prepare.
  # Without it, retention is unknown: stop, keep upload_id + tx_hashes and
  # reconcile before re-preparing or paying again.
  raise unless e.retryable
  stuck = !last_failed.nil? && e.chunks_failed >= last_failed
  raise if attempt >= MAX_ATTEMPTS || stuck    # bounded: same payment, same upload_id
  last_failed = e.chunks_failed
  sleep(2 * attempt)
  retry
end

See docs/external-signer-flow.md section 6 for the daemon contract and examples/07_external_signer.rb for a finalize_with_retry helper.

Examples

See the examples/ directory:

  • 01_connect.rb — Health check
  • 02_data.rb — Public data put/get with cost estimate
  • 03_chunks.rb — Chunk put/get
  • 04_files.rb — File upload and download
  • 06_private_data.rb — Private data put/get
  • 07_external_signer.rb — External-signer prepare/pay/finalize with a bounded partial-upload retry