Raxol.Payments.Mandate.Store (Raxol Payments v0.2.0)

Copy Markdown View Source

Local holder for Xochi Mandate envelopes.

Stores signed envelopes the local node can present (when this raxol is the agent operator) or has issued (when this raxol is the Member). Has no consume semantics -- Xochi's server enforces per-envelope budgets in its own KV. The Store is a pure holder + indexer.

Pattern

A GenServer owns writes; reads bypass through ETS directly with read_concurrency: true.

ETS table names are derived from the GenServer's registered name, so multiple Store instances can coexist on one node by passing a distinct :name to start_link/1:

{:ok, _} = Store.start_link(name: :buyer_mandates)
{:ok, _} = Store.start_link(name: :seller_mandates)
:ok = Store.put(envelope, :buyer_mandates)
mandates = Store.list_for_agent("0x...", :seller_mandates)

When :name is omitted, the Store registers as Raxol.Payments.Mandate.Store (the singleton default).

Primary key: envelope_hash (32-byte binary). Secondary indices:

  • :agent_wallet (bag) -- "which envelopes can this agent present?"
  • :human_wallet (bag) -- "which mandates have I issued?"

Optional DETS

Per-instance DETS persistence via the :dets_path option:

{:ok, _} = Store.start_link(name: :my_store, dets_path: "/var/lib/raxol/my.dets")

For the singleton case, :mandate_store_path in Application config still works as a global default:

config :raxol_payments, mandate_store_path: "/var/lib/raxol_payments/mandates.dets"

An explicit :dets_path opt always wins over the Application config. When neither is set, the Store is in-memory only.

Summary

Functions

Returns a specification to start this module under a supervisor.

Wipe all entries. Intended for tests.

Local-revoke a Mandate (deletes from store; Xochi's KV is unaffected).

Look up a Mandate by its envelope_hash. Direct ETS read.

Return every stored Mandate, in unspecified order.

List every Mandate addressed to the given agent_wallet.

List every Mandate issued by the given human_wallet.

Derived ETS table names. Public for tests + tooling; the runtime uses these internally.

Persist a signed Mandate. Overwrites any prior entry with the same envelope_hash.

Remove every Mandate whose expires_at has passed.

Functions

by_agent_table(server)

@spec by_agent_table(atom()) :: atom()

by_member_table(server)

@spec by_member_table(atom()) :: atom()

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear(server \\ __MODULE__)

@spec clear(GenServer.server()) :: :ok

Wipe all entries. Intended for tests.

delete(envelope_hash, server \\ __MODULE__)

@spec delete(<<_::256>>, GenServer.server()) :: :ok

Local-revoke a Mandate (deletes from store; Xochi's KV is unaffected).

get(envelope_hash, server \\ __MODULE__)

@spec get(<<_::256>>, atom()) :: {:ok, Raxol.Payments.Mandate.t()} | :error

Look up a Mandate by its envelope_hash. Direct ETS read.

handle_manager_cast(msg, state)

Callback implementation for Raxol.Core.Behaviours.BaseManager.handle_manager_cast/2.

handle_manager_info(msg, state)

Callback implementation for Raxol.Core.Behaviours.BaseManager.handle_manager_info/2.

list_all(server \\ __MODULE__)

@spec list_all(atom()) :: [Raxol.Payments.Mandate.t()]

Return every stored Mandate, in unspecified order.

list_for_agent(agent_wallet, server \\ __MODULE__)

@spec list_for_agent(String.t(), atom()) :: [Raxol.Payments.Mandate.t()]

List every Mandate addressed to the given agent_wallet.

list_for_member(human_wallet, server \\ __MODULE__)

@spec list_for_member(String.t(), atom()) :: [Raxol.Payments.Mandate.t()]

List every Mandate issued by the given human_wallet.

primary_table(server)

@spec primary_table(atom()) :: atom()

Derived ETS table names. Public for tests + tooling; the runtime uses these internally.

put(mandate, server \\ __MODULE__)

@spec put(Raxol.Payments.Mandate.t(), GenServer.server()) :: :ok | {:error, term()}

Persist a signed Mandate. Overwrites any prior entry with the same envelope_hash.

start_link(init_opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

sweep_expired(server \\ __MODULE__)

@spec sweep_expired(GenServer.server()) :: non_neg_integer()

Remove every Mandate whose expires_at has passed.