Skip to main content

Operator Network Specification

Operator Network Specification

Overview

Operator Network is designed to listen for, select, and execute cross-chain transactions. The execution process (aka "job") is facilitated by operators who bond HLG, the protocol’s native token, for the right to execute cross-chain transactions. The probability of getting selected to execute a job is based on the number of tokens bonded - the more tokens bonded, the higher the probability of selection. Operators who successfully execute jobs earn transaction fees. Operators who fail to execute jobs will have a number of their bonded tokens slashed.

To operate, participants must configure Holograph CLI on every blockchain they want to execute jobs. To execute jobs, operators must join a pod. Each pod provides a different probability of selection, based on increasingly higher minimum bond requirements. The probability of selection is based on a specific pod being joined, and the number of operators bonded in the specific pod. To move to a different pod, an operator must withdraw and re-bond HLG.

Pods are created in the HolographOperator contract. To join a pod, operators must bond a minimum amount of HLG based on the pod they want to join. Lower number pods require less to bond and accept more operators. Higher number pods require more to bond and accept less operators. Operators can freely choose which pod they want to join. If a pod operator limit is reached, the bond amount increases with each new operator. There is no limit to the number of pods an operator can join. Furthermore, an operator can only participate in one pod per blockchain at any given time.

Operator Job Selection and Execution

Each time a new job is made available, one pod is randomly selected. Once selected, one random operator is selected to finalize the job. When an operator is selected for a job, they are temporarily removed from the pod until the job is finalized. If an operator successfully finalizes a job, they earn a reward and are placed back into their selected pod. If an operator fails to finalize the job, an amount of their bond is slashed and their balance is checked to see if there is enough HLG left to re-bond back to their selected pod. If there is enough HLG to re-bond, the operator is placed back into their selected pod. If there is not enough HLG to re-bond, the operator receives the remainder of their bonded tokens (if any) and is removed from their selected pod. A removed operator can choose to join a new pod at any time in the future, granted they have enough HLG to bond.

In addition to the primary operator, five backup operators are also selected in case the primary operator fails to complete the job. Backup operators are allowed to try and finalize the job in sequence of selection. For example, backup operator n can only try to finalize a job if t * n amount of time has passed since the job was posted. This mechanic allows backup operators to earn the previous operator’s slashed token amount.

Operator jobs are given specific gas limits. This is meant to prevent gas spike abuse (e.g., as a form of DoS attack), bad code, or contract reverts from penalizing good-faith operators. If an operator is late to finalize a job and another operator steps in to take their place, if the gas price is above the set limit, the selected operator will not get slashed. A job is considered successful if it does not revert, or if it reverts but gas limits were followed correctly. Failed jobs can be re-done (for an additional fee), can be returned to origin blockchain (for an additional fee), or left untouched entirely. This shifts the financial responsibility towards users, rather than operators.

Gas limits are embedded in the cross-chain transaction. When bridging tokens, users can specify the max gas limit and gas price and pay the equivalent in native gas tokens. When a job is executed, the portion that executes the bridge request is wrapped by a non-reverting function that sets max gas limit to that of the user’s settings. If the transaction fails due to an out of gas error, or any other error of that kind, it will not revert but instead be logged as a failed job. Gas price is used to check the transaction and see if current gas price is over the set maximum gas price. If the gas is higher, slashing does not occur.

To prevent operator abuse by un-bonding while processing a job, the bond amount is put in escrow and only added back when the job is successfully finalized. In other words, if an operator tries to un-bond while a job has been assigned to them, they would leave only with the remaining tokens and not the ones that were needed for the job.

The following represents a simple OperatorJob object.

struct OperatorJob {
// which pod was selected for this job
pod: number;

// how much time is given to finish the job
// can be different for different networks
blockTimes: number;

// the address of operator that was selected to finalize the job
operator: address;

// the block number when the job was published
// used for easy lookup on-chain
startBlock: number;

// the timestamp of when job was published
// all counters start from that time
startTimestamp: number;

// array of backup operators from selected pod
// referenced by position index in pod
fallbackOperators: number[5];
}

Randomness

In order to make pod and operator selection mathematically random, a random number is generated by running:

sha3(sha3(jobPayload) + jobNonce + blockNumber + blockTimestamp)

The jobNonce is a unique chain-specific number that gets incremented each time a new job request is submitted to the chain. The blockNumber is the number of the mined block in which the transaction was included. The blockTimestamp is the UNIX timestamp that is assigned to each block the moment a miner has mined it. The jobNonce, blockNumber, blockTimestamp, and jobPayload are combined into a single hash, providing enough randomization to make it impossible to calculate, predict, or manipulate the selection process.

To select a pod, this random number is then reduced against the total number of pods available, to choose a pod randomly, expressed as follows:

randomNumber % pods.length

To select an operator, the same random number is then reduced against the total number of operators available in a pod, to choose an operator randomly, expressed as follows:

randomNumber % pods[x].length

Once a pod and an operator have been selected, the random number is used in conjunction with the block hash of a previously mined block, expressed as follows:

unchecked{ random + blockHash(block.number - x) }

To select backup operators, 5 new random numbers are generated inside of the same pod.

Gas Optimizations

Storing data on the blockchain can get expensive really fast. To minimize gas consumption, operator selection logic is calculated in memory when a cross-chain message is received. The resulting calculations are then tightly packed into a 32 byte sized storage slot (the smallest available) using bitwise operations. This data is then saved in a mapping, with the job hash sha3(jobPayload) as the lookup key. This method makes the contract code more complex, but results in very efficient gas usage.

Operator Bond and Pod Calculations

Minimum Bond Amounts are dynamically calculated based on some initial variables. These variables can be changed or set differently for each blockchain.

  • baseBondAmount = 100 * (10^18)
  • podMultiplier = 2
  • operatorThreshold = 1000
  • operatorThresholdStep = 10
  • operatorThresholdMultiplier = 0.01

Pod Threshold is calculated by running this formula with a pod number in question:

operatorThreshold / (2^pod)

Using the above variables and selecting for Pod 1 (or 0 in array language), this would result in the number 1000.

100020\frac{1000}{2^0}

Minimum Bond Amounts are calculated by running this formula with a pod number in question: baseBondAmount * (podMultiplier^pod).

(1001018)20(100*10^{18}) * 2^0

Using the above variables and selecting for Pod 1 (or 0 in array language), this would result in the number 100000000000000000000, or 100 HLG (using 18 decimal places).

Current Bond Amount is calculated by running the Minimum Bond Amount formula above. If the current number of operators in a specific pod is greater than the Pod Threshold, then the Minimum Bond Amount needs to be added with (bondAmount * operatorThresholdMultiplier) * ((position - threshold) / operatorThresholdStep)

Using the above variables and selecting for Pod 1 (or 0 in array language) and getting into position 1500, this would result in the number 150000000000000000000, or 150 HLG.

(1001018)20+(((1001018)20)0.01)(110(1500100020))(100*10^{18}) * 2^0 + (((100*10^{18}) * 2^0) * 0.01)(\frac{1}{10}(1500 - \frac{1000}{2^0}))