The Bid Engine Service is responsible for monitoring the Akash blockchain for deployment orders, evaluating them against provider capabilities, and submitting competitive bids.
Architecture
+----------------------------------------------+| Bid Engine Service || || +---------------------------------------+ || | Order Fetcher | || | - Queries chain for open orders | || | - Pages through results | || | - Handles catchup on restart | || +--------------+------------------------+ || | || v || +---------------------------------------+ || | Event Subscriber | || | - Listens for EventOrderCreated | || | - Receives real-time order events | || +--------------+------------------------+ || | || v || +---------------------------------------+ || | Order Manager (Map) | || | - orders map[orderID]*order | || | - Tracks active order processing | || +--------------+------------------------+ || | || v || +---------------------------------------+ || | Order Instance (per order) | || | - Order lifecycle FSM | || | - Resource matching | || | - Price calculation | || | - Bid submission | || +--------------+------------------------+ || | || v || +---------------------------------------+ || | Pricing Strategy | || | - Shell script pricing | || | - Scale-based pricing | || +---------------------------------------+ |+----------------------------------------------+Service Initialization
When the provider starts, the Bid Engine Service:
- Subscribes to Events - Connects to the event bus
- Starts Provider Attribute Service - Validates provider attributes
- Launches Order Fetcher - Queries chain for existing open orders (catchup)
- Waits for Operators - Ensures hostname/inventory operators are ready
- Begins Processing - Starts handling orders and events
Code Reference: /bidengine/service.go - NewService()
Order Processing
Order Discovery
Orders are discovered in two ways:
1. Real-Time Events (Primary)
case *mtypes.EventOrderCreated: key := mquery.OrderPath(ev.ID) order, err := newOrder(s, ev.ID, s.cfg, s.pass, false) s.orders[key] = order- Listens for
EventOrderCreatedevents from the blockchain - Immediately creates an order manager for the new order
- Begins bid evaluation process
2. Catchup on Startup
func (s *service) ordersFetcher(ctx context.Context, aqc sclient.QueryClient)- Queries the blockchain for all open orders
- Paginates through results (1000 orders per page)
- Catches up on any orders created while provider was offline
- Ensures no orders are missed during restarts
Order Lifecycle
Each order goes through a finite state machine:
1. CREATED ↓2. EVALUATING ↓ (checks pass)3. BIDDING ↓4. BID_SUBMITTED ↓ (lease awarded or order closed)5. COMPLETECode Reference: /bidengine/order.go - order.run()
Bid Evaluation
When an order is detected, the bid engine evaluates:
1. Provider Attributes Match
func shouldBid(order Request, pattr apclient.Attributes) (bool, error)Checks:
- Provider attributes match order requirements
- GPU model/vendor matches (if GPU deployment)
- Region/datacenter matches (if specified)
- Feature support (persistent storage, IP leases, etc.)
Example Order Requirements:
# SDL attributesprofiles: compute: gpu-profile: attributes: region: us-west capabilities/gpu/vendor/nvidia/model/rtx4090: true2. Resource Availability
cluster.Reserve(order.OrderID, order.Resources)Checks:
- Sufficient CPU available
- Sufficient memory available
- Sufficient storage available
- GPU units available (if GPU required)
- Persistent storage available (if requested)
Resource Reservation:
- Resources are reserved (not allocated) when bidding
- Prevents overbidding on limited resources
- Reservation is released if lease is not won
- Reservation is converted to allocation when lease is won
3. Price Calculation
price, err := s.config.BidPricingStrategy.CalculatePrice(ctx, req)Two pricing strategies are supported:
Shell Script Pricing
Custom pricing logic in price_script.sh:
#!/bin/bash# Custom pricing logic# Input: JSON with order details# Output: Price in uakt/block
CPU_PRICE=100MEMORY_PRICE=50STORAGE_PRICE=10
# Calculate total price# ... pricing logic ...echo "$TOTAL_PRICE"Advantages:
- Maximum flexibility
- Can integrate external pricing APIs
- Dynamic pricing based on demand
- Can consider time of day, region, etc.
Scale-Based Pricing
Simple multiplier-based pricing from provider.yaml:
bidpricestoragescale: 1.0bidpricecpuscale: 1.0bidpricememoryscale: 1.0bidpriceendpointscale: 1.0bidpricescriptpath: ""Calculation:
price = (cpu_units * cpu_scale) + (memory_units * memory_scale) + (storage_units * storage_scale) + (endpoint_count * endpoint_scale)Code Reference: /bidengine/pricing.go
Bid Submission
Once evaluation passes, the bid is submitted:
Bid Components
message MsgCreateBid { BidID bid_id = 1; cosmos.base.v1beta1.DecCoin price = 2; cosmos.base.v1beta1.Coin deposit = 3;}Fields:
bid_id- Unique identifier (order ID + provider address)price- Bid price in uakt per blockdeposit- Bid deposit (typically 5 AKT)
Transaction Broadcast
tx := NewMsgCreateBid(order.OrderID, provider, price, deposit)response := txClient.BroadcastTx(tx)Process:
- Create signed transaction
- Broadcast to Akash blockchain
- Wait for transaction confirmation
- Handle success or failure
Bid Timeout
If bid submission fails or times out:
timeout := s.cfg.BidTimeout // default: 5 minutes- Order manager waits for bid timeout duration
- If bid not confirmed within timeout, order processing stops
- Resources are unreserved
- Order manager is removed from active orders
Provider Attribute Validation
The bid engine includes a Provider Attribute Signature Service that validates provider attributes:
Signature Verification
func (s *providerAttrSignatureService) validate(attr apclient.Attributes)Checks:
- Required attributes are present (
host,tier) - Attribute format is valid
- No invalid or malformed attributes
- GPU attributes follow correct naming convention
Automatic Attribute Updates
The service monitors for attribute changes:
case ptypes.ProviderResourcesEvent: // Update provider attributes s.updateAttributes(event.Attributes)Triggers:
- Provider configuration changes
- GPU discovery updates
- Feature enablement (storage, IP leases)
Monitoring & Metrics
Prometheus Metrics
var ordersCounter = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "provider_order_handler",}, []string{"action"})
var orderManagerGauge = promauto.NewGauge(prometheus.GaugeOpts{ Name: "provider_order_manager",})Metrics Exposed:
provider_order_handler{action="start"}- Orders startedprovider_order_handler{action="stop"}- Orders completedprovider_order_manager- Active orders being processed
Status API
func (s *service) Status(ctx context.Context) (*apclient.BidEngineStatus, error)Returns:
{ "orders": 5 // number of active orders}Query Status:
grpcurl -insecure provider.example.com:8444 \ akash.provider.v1.ProviderRPC.GetStatusConfiguration
Bid Engine Config
type Config struct { PricingStrategy PricingStrategy Deposit sdk.Coin BidTimeout time.Duration Attributes []Attribute MaxGroupVolumes int}From provider.yaml:
# Bid deposit (escrow)biddeposit: 5000000uakt # 5 AKT
# Bid timeoutbidtimeout: 5m
# Pricing strategybidpricescriptpath: "/path/to/price_script.sh"# ORbidpricestoragescale: 1.0bidpricecpuscale: 1.0bidpricememoryscale: 1.0
# Max volumes per deploymentmaxgroupvolumes: 20
# Provider attributesattributes: - key: host value: akash - key: tier value: communityError Handling
The bid engine handles various error scenarios:
Chain Query Failures
if errors.Is(err, context.Canceled) { break}// Retry on transient errorscontinue- Retries on network errors
- Graceful shutdown on context cancellation
- Continues processing other orders
Resource Reservation Failures
if err := cluster.Reserve(order.OrderID, resources); err != nil { log.Error("resource reservation failed", "err", err) return // Skip this order}- Skip orders that exceed available resources
- Log reason for rejection
- Continue processing other orders
Bid Submission Failures
if err := SubmitBid(tx); err != nil { log.Error("bid submission failed", "err", err) cluster.Unreserve(order.OrderID) return}- Release reserved resources on failure
- Log error details
- Retry logic for transient failures
Advanced Features
Concurrent Order Processing
Each order is processed in its own goroutine:
order, err := newOrder(s, orderID, s.cfg, s.pass, false)go order.run() // Concurrent executionBenefits:
- Process multiple orders simultaneously
- Don’t block on slow order evaluation
- Maximize bidding throughput
Operator Waiting
Before bidding, the service waits for operators:
err := s.waiter.WaitForAll(ctx)Ensures:
- Hostname operator is ready
- Inventory operator has discovered resources
- IP operator is available (if configured)
Prevents:
- Bidding before resource discovery complete
- Incorrect resource availability calculations
- Missing feature support
Related Documentation
- Cluster Service - Resource management
- Provider Attributes - Attribute configuration
- Provider Installation - Pricing configuration