Coordinator
Git Source (opens in a new tab)
Inherits: ReentrancyGuard
Coordination layer between consuming smart contracts and off-chain Infernet nodes
Implements ReentrancyGuard
to prevent reentrancy in deliverCompute
Allows creating and deleting Subscription
(s)
Allows any address (a node
) to deliver susbcription outputs via off-chain container compute
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]: [160, 32 32, 32] = 256
- [redundancy, containerId, lazy, verifier]: [16, 32, 8, 160] = 216
- [paymentAmount]: [256] = 256
- [paymentToken]: [160] = 160
- [wallet]: [160] = 160
struct Subscription {
address owner;
uint32 activeAt;
uint32 period;
uint32 frequency;
uint16 redundancy;
bytes32 containerId;
bool lazy;
address payable verifier;
uint256 paymentAmount;
address paymentToken;
address payable wallet;
}
ProofRequest
A ProofRequest is a request made to a verifier contract to validate some proof bytes
Tightly-packed struct
- [expiry, nodeWallet]: [32, 160] = 192
- [consumerEscrowed]: [256] = 256
struct ProofRequest {
uint32 expiry;
Wallet nodeWallet;
uint256 consumerEscrowed;
}
State Variables
FEE
Fee registry contract (used to collect protocol fee)
Fee private immutable FEE;
INBOX
Inbox contract (handles lazily storing subscription responses)
Inbox private immutable INBOX;
WALLET_FACTORY
Wallet factory contract (handles validity verification of Wallet
contracts)
WalletFactory private immutable WALLET_FACTORY;
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:readability line and would make Subscriptions harder to grok
mapping(bytes32 => uint16) public redundancyCount;
proofRequests
hash(subscriptionId, interval, caller) => proof request
mapping(bytes32 => ProofRequest) public proofRequests;
subscriptions
subscriptionID => Subscription
1-indexed, 0th-subscription is empty
Visibility restricted to internal
because we expose an explicit getSubscription
view function that returns Subscription
struct
mapping(uint32 => Subscription) internal subscriptions;
Functions
constructor
Initializes new Coordinator
constructor(Registry registry);
Parameters
Name | Type | Description |
---|---|---|
registry | Registry | registry contract |
_calculateFee
Given an input amount
, returns the value of fee
applied to it
function _calculateFee(uint256 amount, uint16 fee) internal pure returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
amount | uint256 | to calculate fee on top of |
fee | uint16 | to use in calculation |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | fee amount |
getSubscription
Returns Subscription
from subscriptions
mapping, indexed by subscriptionId
Useful utility view function because by default public mappings with struct values return destructured parameters
function getSubscription(uint32 subscriptionId) external view returns (Subscription memory);
Parameters
Name | Type | Description |
---|---|---|
subscriptionId | uint32 | subscription ID to collect |
createSubscription
Creates new subscription
function createSubscription(
string memory containerId,
uint32 frequency,
uint32 period,
uint16 redundancy,
bool lazy,
address paymentToken,
uint256 paymentAmount,
address wallet,
address verifier
) external returns (uint32);
Parameters
Name | Type | Description |
---|---|---|
containerId | string | compute container identifier used by off-chain Infernet node |
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 |
lazy | bool | whether to lazily store subscription responses |
paymentToken | address | If providing payment for compute, payment token address (address(0) for ETH, else ERC20 contract address) |
paymentAmount | uint256 | If providing payment for compute, payment in paymentToken per compute request fulfillment |
wallet | address | If providing payment for compute, Infernet Wallet address; msg.sender must be approved spender |
verifier | address | optional verifier contract to restrict payment based on response proof verification |
Returns
Name | Type | Description |
---|---|---|
<none> | uint32 | subscription ID |
cancelSubscription
Cancel a subscription
Must be called by subscriptions[subscriptionId].owner
Cancels subscription by setting Subscription
activeAt
to maximum (technically, de-activating)
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 any address (nodes) to deliver container compute responses for a subscription
Re-entering generally does not work because each node can only call deliverCompute
once per subscription
But, you can call deliverCompute
with a seperate msg.sender
(in same delivery call) so we optimistically restrict with nonReentrant
When Subscription
(s) request lazy responses, stores container output in Inbox
When Subscription
(s) request eager responses, delivers container output directly via BaseConsumer.rawReceiveCompute()
function deliverCompute(
uint32 subscriptionId,
uint32 deliveryInterval,
bytes calldata input,
bytes calldata output,
bytes calldata proof,
address nodeWallet
) public nonReentrant;
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) |
nodeWallet | address | node wallet (used to receive payments, and put up escrow/slashing funds); msg.sender must be authorized spender of wallet |
finalizeProofVerification
Inbound counterpart to IVerifier.requestProofVerification()
to process proof verification
If called by verifier
, accepts valid
to process payout
Else, can be called by anyone after 1 week timeout to side in favor of node by default
function finalizeProofVerification(uint32 subscriptionId, uint32 interval, address node, bool valid) external;
Parameters
Name | Type | Description |
---|---|---|
subscriptionId | uint32 | subscription ID for which proof verification was requested |
interval | uint32 | interval of subscription for which proof verification was requested |
node | address | node in said interval for which proof verification was requested |
valid | bool | true if proof was valid, else false |
Events
SubscriptionCreated
Emitted when a new subscription is created
event SubscriptionCreated(uint32 indexed id);
Parameters
Name | Type | Description |
---|---|---|
id | uint32 | subscription ID |
SubscriptionCancelled
Emitted when a subscription is cancelled
event SubscriptionCancelled(uint32 indexed id);
Parameters
Name | Type | Description |
---|---|---|
id | uint32 | subscription ID |
SubscriptionFulfilled
Emitted when a subscription is fulfilled
event SubscriptionFulfilled(uint32 indexed id, address indexed node);
Parameters
Name | Type | Description |
---|---|---|
id | uint32 | subscription ID |
node | address | address of fulfilling node |
Errors
InvalidWallet
Thrown by deliverCompute()
if attempting to use an invalid wallet
or one not created by WalletFactory
4-byte signature: 0x23455ba1
error InvalidWallet();
IntervalMismatch
Thrown by deliverCompute()
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 deliverCompute()
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();
UnauthorizedVerifier
Thrown by finalizeProofVerification()
if called by a msg.sender
that is unauthorized to finalize proof
When a proof request is expired, this can be any address; until then, this is the designated verifier
address
4-byte signature: 0xb9857aa1
error UnauthorizedVerifier();
NodeRespondedAlready
Thrown by deliverCompute()
if node
has already responded this interval
4-byte signature: 0x88a21e4f
error NodeRespondedAlready();
SubscriptionNotFound
Thrown by deliverCompute()
if attempting to access a subscription that does not exist
4-byte signature: 0x1a00354f
error SubscriptionNotFound();
ProofRequestNotFound
Thrown by finalizeProofVerification()
if attempting to access a proof request that does not exist
4-byte signature: 0x1d68b37c
error ProofRequestNotFound();
NotSubscriptionOwner
Thrown by cancelSubscription()
if attempting to modify a subscription not owned by caller
4-byte signature: 0xa7fba711
error NotSubscriptionOwner();
SubscriptionCompleted
Thrown by deliverCompute()
if attempting to deliver a completed subscription
4-byte signature: 0xae6704a7
error SubscriptionCompleted();
SubscriptionNotActive
Thrown by deliverCompute()
if attempting to deliver a subscription before activeAt
4-byte signature: 0xefb74efe
error SubscriptionNotActive();
UnsupportedVerifierToken
Thrown by deliverCompute
if attempting to pay a IVerifier
-contract in a token it does not support receiving payments in
4-byte signature: 0xe2372799
error UnsupportedVerifierToken();