Documentation
¶
Overview ¶
Package chain provides clients for interacting with the Manifest blockchain.
Client ¶
The Client provides gRPC-based access to chain queries and transactions:
- Query leases by UUID, provider, or state; query credit accounts and provider withdrawable balances
- Submit transactions: acknowledge / reject / close leases, withdraw funds by provider
- Wait for transaction confirmation with retry logic
- Ping for health checks
All query operations support context cancellation and include Prometheus metrics.
EventSubscriber ¶
The EventSubscriber connects to CometBFT's WebSocket interface to receive real-time lease events:
- lease_created: New lease for this provider
- lease_closed: Lease was closed by tenant
- lease_expired: Lease expired (time limit)
- lease_auto_closed: Lease auto-closed due to credit exhaustion
The subscriber uses a fan-out pattern where multiple consumers can subscribe independently. Each consumer gets its own buffered channel.
Signer ¶
The Signer handles transaction signing using the Cosmos SDK keyring:
- Loads keys from file, OS keychain, or test backend
- Signs transactions with proper chain ID and account info
- Provides the provider's bech32 address
Error Handling ¶
The package defines ChainTxError for wrapping Cosmos SDK transaction errors. This enables error inspection with errors.Is() for specific error codes like insufficient funds or invalid sequence.
Reconnection ¶
The EventSubscriber implements automatic reconnection with exponential backoff:
Initial: 1 second Max: 60 seconds Multiplier: 2x
The Client uses retry logic for transient gRPC errors (Unavailable, Unknown).
Index ¶
- Constants
- func EnsureFunding(ctx context.Context, bankQ bankQuerier, broadcaster txBroadcaster, ...) error
- func EnsureGrants(ctx context.Context, authzQ authzQuerier, broadcaster txBroadcaster, ...) error
- func SignWithPrivKey(ctx context.Context, signMode signing.SignMode, ...) (signing.SignatureV2, error)
- type ChainTxError
- func (e *ChainTxError) Error() string
- func (e *ChainTxError) ExpectedSequence() (uint64, bool)
- func (e *ChainTxError) Is(target error) bool
- func (e *ChainTxError) IsInsufficientFee() bool
- func (e *ChainTxError) IsOutOfGas() bool
- func (e *ChainTxError) IsSequenceMismatch() bool
- func (e *ChainTxError) IsTxInMempool() bool
- type Client
- func (c *Client) AcknowledgeLeases(ctx context.Context, leaseUUIDs []string) (uint64, []string, error)
- func (c *Client) Close() error
- func (c *Client) CloseLeases(ctx context.Context, leaseUUIDs []string, reason string) (uint64, []string, error)
- func (c *Client) Conn() *grpc.ClientConn
- func (c *Client) GetActiveLease(ctx context.Context, leaseUUID string) (*billingtypes.Lease, error)
- func (c *Client) GetActiveLeasesByProvider(ctx context.Context, providerUUID string) ([]billingtypes.Lease, error)
- func (c *Client) GetCreditAccount(ctx context.Context, tenant string) (*billingtypes.CreditAccount, sdktypes.Coins, error)
- func (c *Client) GetLease(ctx context.Context, leaseUUID string) (*billingtypes.Lease, error)
- func (c *Client) GetPendingLeases(ctx context.Context, providerUUID string) ([]billingtypes.Lease, error)
- func (c *Client) GetProvider(ctx context.Context, providerUUID string) (*skutypes.Provider, error)
- func (c *Client) GetProviderWithdrawable(ctx context.Context, providerUUID string) (sdktypes.Coins, error)
- func (c *Client) Ping(ctx context.Context) error
- func (c *Client) RejectLeases(ctx context.Context, leaseUUIDs []string, reason string) (uint64, []string, error)
- func (c *Client) WithdrawByProvider(ctx context.Context, providerUUID string, key []byte) (string, *billingtypes.MsgWithdrawResponse, error)
- type ClientConfig
- type EventSubscriber
- type EventSubscriberConfig
- type LeaseEvent
- type LeaseEventType
- type Signer
- type SignerBalanceCollector
- type SignerConfig
- type SignerPool
- func (p *SignerPool) Acquire() (*Signer, bool, func())
- func (p *SignerPool) DemoteToSingleSigner()
- func (p *SignerPool) HasSubSigners() bool
- func (p *SignerPool) LaneCount() int
- func (p *SignerPool) Primary() *Signer
- func (p *SignerPool) ProviderAddress() string
- func (p *SignerPool) Size() int
- func (p *SignerPool) SubSignerAddresses() []string
- type SignerPoolConfig
Constants ¶
const ( // DefaultEventChannelCapacity is the default buffer size for subscriber channels. // If the channel fills up, events will be dropped with a warning log. // Monitor fred_events_dropped_total metric for capacity issues. DefaultEventChannelCapacity = 1000 )
Variables ¶
This section is empty.
Functions ¶
func EnsureFunding ¶
func EnsureFunding(ctx context.Context, bankQ bankQuerier, broadcaster txBroadcaster, pool *SignerPool, minBalance, topUpAmount sdk.Coin) error
EnsureFunding tops up sub-signers that are below the minimum balance.
func EnsureGrants ¶
func EnsureGrants(ctx context.Context, authzQ authzQuerier, broadcaster txBroadcaster, pool *SignerPool) error
EnsureGrants creates any missing authz grants from the provider (granter) to each sub-signer (grantee) for the billing message types.
func SignWithPrivKey ¶
func SignWithPrivKey( ctx context.Context, signMode signing.SignMode, signerData authsigning.SignerData, txBuilder client.TxBuilder, kr keyring.Keyring, keyName string, txConfig client.TxConfig, sequence uint64, ) (signing.SignatureV2, error)
SignWithPrivKey signs using the keyring.
Types ¶
type ChainTxError ¶
type ChainTxError struct {
Code uint32 // ABCI error code
Codespace string // Module that generated the error
RawLog string // Human-readable error message
}
ChainTxError represents an error from a failed chain transaction. It captures the ABCI error code and codespace, allowing callers to check against specific module errors using errors.Is().
func (*ChainTxError) Error ¶
func (e *ChainTxError) Error() string
Error implements the error interface.
func (*ChainTxError) ExpectedSequence ¶
func (e *ChainTxError) ExpectedSequence() (uint64, bool)
ExpectedSequence extracts the expected sequence number from a sequence mismatch error. Returns 0, false if the error is not a sequence mismatch or the expected sequence cannot be parsed.
func (*ChainTxError) Is ¶
func (e *ChainTxError) Is(target error) bool
Is implements errors.Is() interface to check against cosmossdk.io/errors registered errors. This allows checking: errors.Is(err, billingtypes.ErrLeaseNotPending)
func (*ChainTxError) IsInsufficientFee ¶
func (e *ChainTxError) IsInsufficientFee() bool
IsInsufficientFee returns true if this error is an insufficient-fee error (SDK code 13).
func (*ChainTxError) IsOutOfGas ¶
func (e *ChainTxError) IsOutOfGas() bool
IsOutOfGas returns true if this error is an out-of-gas error (SDK code 11).
func (*ChainTxError) IsSequenceMismatch ¶
func (e *ChainTxError) IsSequenceMismatch() bool
IsSequenceMismatch returns true if this error is a sequence mismatch (SDK code 32).
func (*ChainTxError) IsTxInMempool ¶
func (e *ChainTxError) IsTxInMempool() bool
IsTxInMempool returns true if this error indicates the transaction is already in the mempool cache (SDK code 19). This happens when waitForTx times out on a previous attempt and the retry re-signs with the same sequence, producing identical tx bytes whose hash is still cached in the CometBFT mempool.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client provides methods to query and submit transactions to the chain.
func NewClient ¶
func NewClient(cfg ClientConfig, pool *SignerPool) (*Client, error)
NewClient creates a new chain client connected to the given gRPC endpoint. The pool parameter is required; pass a single-signer pool for backward compatibility.
func (*Client) AcknowledgeLeases ¶
func (c *Client) AcknowledgeLeases(ctx context.Context, leaseUUIDs []string) (uint64, []string, error)
AcknowledgeLeases acknowledges the given leases. Returns the number of leases acknowledged and tx hashes.
func (*Client) CloseLeases ¶
func (c *Client) CloseLeases(ctx context.Context, leaseUUIDs []string, reason string) (uint64, []string, error)
CloseLeases closes the given leases with an optional reason. Returns the number of leases closed and tx hashes.
func (*Client) Conn ¶
func (c *Client) Conn() *grpc.ClientConn
Conn returns the underlying gRPC connection for creating query clients.
func (*Client) GetActiveLease ¶
GetActiveLease returns an active lease by UUID, or nil if not found or not active.
func (*Client) GetActiveLeasesByProvider ¶
func (c *Client) GetActiveLeasesByProvider(ctx context.Context, providerUUID string) ([]billingtypes.Lease, error)
GetActiveLeasesByProvider returns all active leases for a provider.
func (*Client) GetCreditAccount ¶
func (c *Client) GetCreditAccount(ctx context.Context, tenant string) (*billingtypes.CreditAccount, sdktypes.Coins, error)
GetCreditAccount returns the credit account and balances for a tenant.
func (*Client) GetLease ¶
GetLease returns a lease by UUID regardless of state. Returns nil if the lease is not found.
func (*Client) GetPendingLeases ¶
func (c *Client) GetPendingLeases(ctx context.Context, providerUUID string) ([]billingtypes.Lease, error)
GetPendingLeases returns all pending leases for a provider.
func (*Client) GetProvider ¶
GetProvider returns provider details by UUID.
func (*Client) GetProviderWithdrawable ¶
func (c *Client) GetProviderWithdrawable(ctx context.Context, providerUUID string) (sdktypes.Coins, error)
GetProviderWithdrawable returns the total withdrawable amounts for a provider. The ProviderWithdrawable query is paginated over active leases, so we page through all results and sum the amounts for the full total.
func (*Client) Ping ¶
Ping checks if the chain connection is healthy by querying the signer's account. Returns nil if healthy, otherwise returns the error.
func (*Client) RejectLeases ¶
func (c *Client) RejectLeases(ctx context.Context, leaseUUIDs []string, reason string) (uint64, []string, error)
RejectLeases rejects the given pending leases with an optional reason. Returns the number of leases rejected and tx hashes. This is used when provisioning fails and the provider cannot fulfill the lease.
func (*Client) WithdrawByProvider ¶
func (c *Client) WithdrawByProvider(ctx context.Context, providerUUID string, key []byte) (string, *billingtypes.MsgWithdrawResponse, error)
WithdrawByProvider withdraws funds from one page of a provider's active leases and returns the transaction hash and the decoded response.
Provider-wide withdrawal is paginated by an opaque cursor: pass an empty key to start from the beginning, then echo back the returned NextKey to advance to the next page. NextKey is empty once every active lease has been settled (equivalently, MsgWithdrawResponse.HasMore is false). The cursor is opaque — it is passed back verbatim, never parsed. Callers loop until NextKey is empty (see the WithdrawScheduler); the per-cycle bound lives there, not here.
Limit is the configured withdrawLimit (leases settled per page); the scheduler pages via the cursor, so this only trades settlement-tx count against per-tx gas.
type ClientConfig ¶
type ClientConfig struct {
Endpoint string
TLSEnabled bool
TLSCAFile string // Path to CA certificate file (optional, uses system CAs if empty)
TLSSkipVerify bool // Skip certificate verification (for testing only)
TxPollInterval time.Duration // Interval for polling tx status (default: 500ms)
TxTimeout time.Duration // Timeout for waiting for tx inclusion (default: 60s)
QueryPageLimit int // Page limit for paginated queries (default: 100)
WithdrawLimit int // Leases settled per provider-wide MsgWithdraw page (default: 100; the chain rejects > MaxBatchLeaseSize)
}
ClientConfig holds configuration for the chain client.
type EventSubscriber ¶
type EventSubscriber struct {
// contains filtered or unexported fields
}
EventSubscriber handles WebSocket subscription to CometBFT events. It supports multiple consumers via Subscribe() - each subscriber gets its own channel and receives all events (fan-out pattern).
func NewEventSubscriber ¶
func NewEventSubscriber(cfg EventSubscriberConfig) (*EventSubscriber, error)
NewEventSubscriber creates a new event subscriber.
func (*EventSubscriber) Close ¶
func (s *EventSubscriber) Close()
Close shuts down the event subscriber and closes all subscriber channels. Safe to call multiple times - subsequent calls are no-ops.
func (*EventSubscriber) Start ¶
func (s *EventSubscriber) Start(ctx context.Context) error
Start begins listening for events. It will automatically reconnect on failure.
func (*EventSubscriber) Subscribe ¶
func (s *EventSubscriber) Subscribe() chan LeaseEvent
Subscribe creates a new subscription and returns a channel that receives all events. Each subscriber gets its own buffered channel. Call Unsubscribe when done to avoid leaks. Returns a bidirectional channel so it can be passed to Unsubscribe for cleanup. Returns nil if the subscriber has been closed.
func (*EventSubscriber) Unsubscribe ¶
func (s *EventSubscriber) Unsubscribe(ch chan LeaseEvent)
Unsubscribe removes a subscription and closes its channel. Safe to call multiple times or with a nil channel.
type EventSubscriberConfig ¶
type EventSubscriberConfig struct {
URL string
ProviderUUID string
PingInterval time.Duration
ReconnectInitial time.Duration
ReconnectMax time.Duration
ChannelCapacity int // Buffer size for subscriber channels (default: 1000)
}
EventSubscriberConfig holds configuration for the event subscriber.
type LeaseEvent ¶
type LeaseEvent struct {
Type LeaseEventType
LeaseUUID string
ProviderUUID string
Tenant string
}
LeaseEvent represents a lease-related event from the chain.
type LeaseEventType ¶
type LeaseEventType string
LeaseEventType represents the type of lease event.
const ( LeaseCreated LeaseEventType = "lease_created" LeaseAcknowledged LeaseEventType = "lease_acknowledged" LeaseRejected LeaseEventType = "lease_rejected" LeaseClosed LeaseEventType = "lease_closed" LeaseExpired LeaseEventType = "lease_expired" LeaseAutoClosed LeaseEventType = "lease_auto_closed" // Credit exhaustion (any provider) )
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
Signer handles transaction signing using a Cosmos keyring.
func (*Signer) BuildSimTx ¶ added in v0.6.0
func (s *Signer) BuildSimTx(msgs []sdk.Msg, accountAny *codectypes.Any, declaredGas uint64) ([]byte, error)
BuildSimTx builds an encoded, zero-fee, dummy-signed tx suitable for the Simulate RPC. The signature is empty (Simulate skips sig verification) but uses the real pubkey + the account's on-chain sequence (Simulate DOES check the sequence). Callers pass simDeclaredGas as declaredGas (see the const).
We deliberately do NOT reuse the SDK's clienttx.CalculateGas / Factory.BuildSimTx: they require a tx.Factory + client.Context that fred's custom Signer/keyring never constructs, and CalculateGas applies the adjustment via uint64(adj*float64(GasUsed)) — the float→uint64 truncation fred deliberately replaced with deterministic cosmossdk.io/math (see adjustGas).
func (*Signer) FallbackGas ¶ added in v0.6.0
FallbackGas returns the Simulate-failure fallback gas: the configured gas_limit run through the same adjustment+cap as the success path. This is the single source of truth for the degraded path (byte-for-byte the legacy effective default), used by the broadcast pre-seed when Simulate is down.
type SignerBalanceCollector ¶
type SignerBalanceCollector struct {
// contains filtered or unexported fields
}
SignerBalanceCollector implements prometheus.Collector and emits the `fred_signer_balance` gauge on every /metrics scrape.
On Collect, the pool is snapshotted (live reads of ProviderAddress and SubSignerAddresses), one balance query is fanned out per address against the bank module, and each successful response yields one gauge series. The balance (a math.Int) is emitted as a float64 via a big.Float intermediate with no int64 round-trip, so balances above math.MaxInt64 (e.g. the ~1e29 umfx dev provider) produce a healthy series instead of being misclassified as a failure; float64 is exact for integers <= 2^53, with negligible relative rounding above that for threshold alerting. The configured denom is both queried from bank and emitted as the `denom` label so the gauge is accurate on any deployment (not just umfx-denominated networks). Per-address query failures — a bank RPC error or a nil/empty response, never a value-representation condition — drop only that address's series and bump metrics.SignerBalanceQueryFailures (no per-index counter cardinality).
The collector is intentionally stateless across scrapes — it does not cache balances, does not run a background sampler, and does not allocate goroutines outside of Collect. This means the gauge naturally tracks the live pool (e.g. after DemoteToSingleSigner) without any extra wiring.
func NewSignerBalanceCollector ¶
func NewSignerBalanceCollector(bankQ bankQuerier, pool *SignerPool, denom string, scrapeTimeout time.Duration) *SignerBalanceCollector
NewSignerBalanceCollector constructs a per-scrape signer balance collector. denom is the bank module denom to query (e.g. "umfx") and is also emitted as the `denom` label on each series so the metric stays accurate when the network uses a non-umfx fee denom. scrapeTimeout bounds the wall time of a single Collect call across all fanned-out queries.
func (*SignerBalanceCollector) Collect ¶
func (c *SignerBalanceCollector) Collect(ch chan<- prometheus.Metric)
Collect implements prometheus.Collector. Safe for concurrent calls.
func (*SignerBalanceCollector) Describe ¶
func (c *SignerBalanceCollector) Describe(ch chan<- *prometheus.Desc)
Describe implements prometheus.Collector.
type SignerConfig ¶
type SignerConfig struct {
KeyringBackend string
KeyringDir string
KeyName string
ChainID string
GasLimit uint64
MaxGasLimit uint64 // 0 = no cap; if set, caps the gas limit during out-of-gas retries
GasAdjustment float64 // multiplier applied to GasLimit at sign time (Cosmos CLI convention); 0 or 1.0 = no adjustment
GasPrice int64
FeeDenom string
Passphrase string // Keyring passphrase for "file" backend (read from FRED_KEYRING_PASSPHRASE env)
}
SignerConfig holds configuration for the transaction signer.
type SignerPool ¶
type SignerPool struct {
// contains filtered or unexported fields
}
SignerPool manages a primary signer and optional sub-signers for parallel transaction broadcasting via CosmosSDK authz.
func NewSignerPool ¶
func NewSignerPool(cfg SignerPoolConfig) (*SignerPool, error)
NewSignerPool creates a signer pool with a primary signer and optional sub-signers. Sub-signer keys are derived from the mnemonic on first boot, and loaded from the keyring on subsequent boots.
func (*SignerPool) Acquire ¶
func (p *SignerPool) Acquire() (*Signer, bool, func())
Acquire returns the next available sub-signer with exclusive access. If no sub-signers exist, returns the primary signer (no locking needed). The caller MUST call the returned release function when done; failing to do so will deadlock the pool.
func (*SignerPool) DemoteToSingleSigner ¶
func (p *SignerPool) DemoteToSingleSigner()
DemoteToSingleSigner removes all sub-signers, making the pool primary-only. Thread-safe, but must be called before the AckBatcher is constructed since it captures lane count at construction time.
func (*SignerPool) HasSubSigners ¶
func (p *SignerPool) HasSubSigners() bool
HasSubSigners returns true if the pool has any sub-signers.
func (*SignerPool) LaneCount ¶
func (p *SignerPool) LaneCount() int
LaneCount returns the number of parallel lanes for the batcher. With 0 sub-signers → 1 lane (primary). With N sub-signers → N lanes.
func (*SignerPool) Primary ¶
func (p *SignerPool) Primary() *Signer
Primary returns the primary signer. Used for operations that must use the provider key (withdrawals, grants, funding).
func (*SignerPool) ProviderAddress ¶
func (p *SignerPool) ProviderAddress() string
ProviderAddress returns the primary signer's address.
func (*SignerPool) Size ¶
func (p *SignerPool) Size() int
Size returns the total number of signers (1 primary + N sub-signers).
func (*SignerPool) SubSignerAddresses ¶
func (p *SignerPool) SubSignerAddresses() []string
SubSignerAddresses returns the addresses of all sub-signers.
type SignerPoolConfig ¶
type SignerPoolConfig struct {
SignerConfig
SubSignerCount int // 0 = single signer (default)
Mnemonic string // only used on first boot to derive sub-signer keys
}
SignerPoolConfig extends SignerConfig with sub-signer options.