Coordinator
Git Source (opens in a new tab)
Inherits: Manager
Coordination layer between consuming smart contracts and off-chain Infernet nodes
Allows creating and deleting Subscription
(s)
Allows nodes with Manager.NodeStatus.Active
to deliver subscription outputs via off-chain container compute
State Variables
DELIVERY_OVERHEAD_WEI
Gas overhead in wei to deliver container compute responses
This is the additional cost of any validation checks performed within the Coordinator
before delivering responses to consumer contracts
A uint16 is sufficient but we are not packing variables so control plane cost is higher because of type casting during operations. Thus, we can just stick to uint256
uint256 public constant DELIVERY_OVERHEAD_WEI = 56_600 wei;
id
Current highest subscription ID
1-indexed to allow using id as a mapping value (prevent 0-indexed default from being misused)
uint32 size(4.2B) should be sufficiently large
uint32 public id = 1;
nodeResponded
hash(subscriptionId, interval, caller) => has caller responded for (sub, interval)?
mapping(bytes32 => bool) public nodeResponded;
redundancyCount
hash(subscriptionId, interval) => Number of responses for (sub, interval)?
Limited to type(Subscription.redundancy) == uint16
Technically, this is not required and we can save an SLOAD if we simply add a uint48 to the subscription struct that represents 32 bits of the interval -> 16 bits of redundancy count, reset each interval change But, this is a little over the optimization:redability line and would make Subscriptions harder to grok
mapping(bytes32 => uint16) public redundancyCount;
subscriptions
subscriptionID => Subscription
1-indexed, 0th-subscription is empty
mapping(uint32 => Subscription) public subscriptions;
Functions
_deliverComputeWithOverhead
Internal counterpart to deliverCompute()
w/ ability to set custom gas overhead allowance
When called by deliverCompute()
, callingOverheadWei == 0
because no additional overhead imposed
_When called by deliverComputeDelegatee()
, DELEGATEE*OVERHEAD*_\_WEI
is imposed*
function _deliverComputeWithOverhead(
uint32 subscriptionId,
uint32 deliveryInterval,
bytes calldata input,
bytes calldata output,
bytes calldata proof,
uint256 callingOverheadWei
) internal;
Parameters
Name | Type | Description |
---|---|---|
subscriptionId | uint32 | subscription ID to deliver |
deliveryInterval | uint32 | subscription interval to deliver |
input | bytes | optional off-chain input recorded by Infernet node (empty, hashed input, processed input, or both) |
output | bytes | optional off-chain container output (empty, hashed output, processed output, both, or fallback: all encodeable data) |
proof | bytes | optional container execution proof (or arbitrary metadata) |
callingOverheadWei | uint256 | additional overhead gas used for delivery |
createSubscription
Creates new subscription
function createSubscription(
string memory containerId,
bytes calldata inputs,
uint48 maxGasPrice,
uint32 maxGasLimit,
uint32 frequency,
uint32 period,
uint16 redundancy
) external returns (uint32);
Parameters
Name | Type | Description |
---|---|---|
containerId | string | compute container identifier used by off-chain Infernet node |
inputs | bytes | optional container inputs |
maxGasPrice | uint48 | max gas price in wei paid by an Infernet node when fulfilling callback |
maxGasLimit | uint32 | max gas limit in wei paid by an Infernet node in callback tx |
frequency | uint32 | max number of times to process subscription (i.e, frequency == 1 is a one-time request) |
period | uint32 | period, in seconds, at which to progress each responding interval |
redundancy | uint16 | number of unique responding Infernet nodes |
Returns
Name | Type | Description |
---|---|---|
<none> | uint32 | subscription ID |
cancelSubscription
Cancel a subscription
Must be called by subscriptions[subscriptionId].owner
function cancelSubscription(uint32 subscriptionId) external;
Parameters
Name | Type | Description |
---|---|---|
subscriptionId | uint32 | subscription ID to cancel |
getSubscriptionInterval
Calculates subscription interval
based on activeAt
and period
function getSubscriptionInterval(uint32 activeAt, uint32 period) public view returns (uint32);
Parameters
Name | Type | Description |
---|---|---|
activeAt | uint32 | when does a subscription start accepting callback responses |
period | uint32 | time, in seconds, between each subscription response interval |
Returns
Name | Type | Description |
---|---|---|
<none> | uint32 | current subscription interval |
deliverCompute
Allows nodes with Manager.NodeStatus.Active
to deliver container compute responses for a subscription
Re-entering does not work because only active nodes (max 1 response) can call deliverCompute
Re-entering and delivering via a seperate node msg.sender
works but is ignored in favor of explicit maxGasLimit
For containers without succinctly-verifiable proofs, the proof
field can be repurposed for arbitrary metadata
Enforces an overhead delivery cost of DELIVERY_OVERHEAD_WEI
and 0
additional overhead
function deliverCompute(
uint32 subscriptionId,
uint32 deliveryInterval,
bytes calldata input,
bytes calldata output,
bytes calldata proof
) external onlyActiveNode;
Parameters
Name | Type | Description |
---|---|---|
subscriptionId | uint32 | subscription ID to deliver |
deliveryInterval | uint32 | subscription interval to deliver |
input | bytes | optional off-chain container input recorded by Infernet node (empty, hashed input, processed input, or both) |
output | bytes | optional off-chain container output (empty, hashed output, processed output, both, or fallback: all encodeable data) |
proof | bytes | optional off-chain container execution proof (or arbitrary metadata) |
Events
SubscriptionCreated
Emitted when a new subscription is created
event SubscriptionCreated(uint32 indexed id);
SubscriptionCancelled
Emitted when a subscription is cancelled
event SubscriptionCancelled(uint32 indexed id);
SubscriptionFulfilled
Emitted when a subscription is fulfilled
event SubscriptionFulfilled(uint32 indexed id, address indexed node);
Errors
GasPriceExceeded
Thrown by deliverComputeWithOverhead()
if delivering tx with gasPrice > subscription maxGasPrice
E.g. submitting tx with gas price 10 gwei
when network basefee is 11 gwei
4-byte signature: 0x682bad5a
error GasPriceExceeded();
GasLimitExceeded
Thrown by deliverComputeWithOverhead()
if delivering tx with consumed gas > subscription maxGasLimit
E.g. submitting tx with gas consumed 200_000 wei
when max allowed by subscription is 175_000 wei
4-byte signature: 0xbe9179a6
error GasLimitExceeded();
IntervalMismatch
Thrown by deliverComputeWithOverhead()
if attempting to deliver container compute response for non-current interval
E.g submitting tx for interval
< current (period elapsed) or interval
> current (too early to submit)
4-byte signature: 0x4db310c3
error IntervalMismatch();
IntervalCompleted
Thrown by deliverComputeWithOverhead()
if redundancy
has been met for current interval
E.g submitting 4th output tx for a subscription with redundancy == 3
4-byte signature: 0x2f4ca85b
error IntervalCompleted();
NodeRespondedAlready
Thrown by deliverComputeWithOverhead()
if node
has already responded this interval
4-byte signature: 0x88a21e4f
error NodeRespondedAlready();
SubscriptionNotFound
Thrown by deliverComputeWithOverhead()
if attempting to access a subscription that does not exist
4-byte signature: 0x1a00354f
error SubscriptionNotFound();
NotSubscriptionOwner
Thrown by cancelSubscription()
if attempting to modify a subscription not owned by caller
4-byte signature: 0xa7fba711
error NotSubscriptionOwner();
SubscriptionCompleted
Thrown by deliverComputeWithOverhead()
if attempting to deliver a completed subscription
4-byte signature: 0xae6704a7
error SubscriptionCompleted();
SubscriptionNotActive
Thrown by deliverComputeWithOverhead()
if attempting to deliver a subscription before activeAt
4-byte signature: 0xefb74efe
error SubscriptionNotActive();
Structs
Subscription
A subscription is the fundamental unit of Infernet
A subscription represents some request configuration for off-chain compute via containers on Infernet nodes
A subscription with frequency == 1
is a one-time subscription (a callback)
A subscription with frequency > 1
is a recurring subscription (many callbacks)
*Tightly-packed struct:
- [owner, activeAt, period, frequency]: [32, 160, 32, 32] = 256
- [redundancy, maxGasPrice, maxGasLimit]: [16, 48, 32] = 96*
struct Subscription {
/// @notice Subscription owner + recipient
/// @dev This is the address called to fulfill a subscription request and must inherit `BaseConsumer`
/// @dev Default initializes to `address(0)`
address owner;
/// @notice Timestamp when subscription is first active and an off-chain Infernet node can respond
/// @dev When `period == 0`, the subscription is immediately active
/// @dev When `period > 0`, subscription is active at `createdAt + period`
uint32 activeAt;
/// @notice Time, in seconds, between each subscription interval
/// @dev At worst, assuming subscription occurs once/year << uint32
uint32 period;
/// @notice Number of times a subscription is processed
/// @dev At worst, assuming 30 req/min * 60 min * 24 hours * 365 days * 10 years << uint32
uint32 frequency;
/// @notice Number of unique nodes that can fulfill a subscription at each `interval`
/// @dev uint16 allows for >255 nodes (uint8) but <65,535
uint16 redundancy;
/// @notice Max gas price in wei paid by an Infernet node when fulfilling callback
/// @dev uint40 caps out at ~1099 gwei, uint48 allows up to ~281K gwei
uint48 maxGasPrice;
/// @notice Max gas limit in wei used by an Infernet node when fulfilling callback
/// @dev Must be at least equal to the gas limit of your receiving function execution + DELIVERY_OVERHEAD_WEI
/// @dev uint24 is too small at ~16.7M (<30M mainnet gas limit), but uint32 is more than enough (~4.2B wei)
uint32 maxGasLimit;
/// @notice Container identifier used by off-chain Infernet nodes to determine which container is used to fulfill a subscription
/// @dev Can be used to specify a linear DAG of containers by seperating container names with a "," delimiter ("A,B,C")
/// @dev Better represented by a string[] type but constrained to string to keep struct and functions simple
string containerId;
/// @notice Optional container input parameters
/// @dev If left empty, off-chain Infernet nodes call public view fn: `BaseConsumer(owner).getContainerInputs()`
bytes inputs;
}