How Slinky can prevent oracle manipulation attacks

Authors: @marco @hxrts @mag

In late December an attack on Levana resulted in $1.146M being extracted, which at the time accounted for approximately 10% of the total value locked across all Levana liquidity pools. The attack occurred intermittently over the course of 13 days, from December 13th to 26th, with the extracted value from the last day accounting for approximately 5% of all Levana’s liquidity.

This attack illustrates the intrinsic vulnerabilities of 3rd party oracles that rely on the submission of special “oracle-update” transactions. In this post we start by describing Slinky‘s unique architecture. We then discuss the specific mechanics of the attack, as well as the general vulnerabilities of transaction-based oracles. Finally, we discuss Slinky’s security properties, in particular how Slinky’s security assurances entirely mitigate the attack suffered by Levana.

The attack was described previously in Levana’s recent post-mortem, and elaborated in Range’s subsequent analysis. These two resources were fundamental to the writing of this post. We would like to thank in particular Andres from Range for a productive dialogue that contributed to the description of the attack’s mechanics.

Slinky’s enshrined oracle solution vs transaction-based oracles

Slinky is an enshrined oracle that is built on top of the CosmosSDK using CometBFT’s Vote Extensions functionality, which allows Slinky to be run in-protocol by the validator set of the chain. Vote Extensions are additional data blobs that each validator can add to their consensus vote to be delivered to the network, facilitating censorship resistance. In contrast to transaction-based oracles, Slinky’s production of oracle prices does not rely on any outside actors, apart from the data originator, rather price inputs are fetched and aggregated entirely within the consensus process.

Slinky’s architecture

Slinky’s oracle mechanism occurs over the span of two blocks. At height n-1 all validators submit data using the Extend Vote method, then at height n the next proposer produces a price and all validators verify the computation.

Slinky’s final aggregated price is a deterministic stake-weighted median, computed locally by the proposer using Vote Extension data from block n-1 corresponding to 2/3+ voting power. The output of this calculation is then included in block n and the result is verified by all validators during block processing.

The stake-weighted median calculation first arranges the list of prices in monotonically increasing order, then partitions the list into two groups where the sum of weights is as equal as possible. The median is then the price point that best divides these two weighted groups. A weighted median measures central tendency while remaining robust to outliers, and in the context of prices submitted by a weighted set of validators, ensures the aggregate price is resistant to manipulation.

Consider an example where four validators post updates in their Vote Extensions of $10, $15, $15.1, and $1000.

Validators 1 and 2 have \frac{1}{3} of stake, while validators 3 and 4 control only \frac{1}{100} of stake. The total stake published through Vote Extensions is \frac{206}{300}. The weighted median calculation is performed on the fraction of stake that each validator contributes across all Vote Extensions, which here is \frac{206}{300}. We’ll call this the “committed stake.”

Validators 1 and 2 each contribute almost half of committed stake, while validators 3 and 4 contribute significantly less. The stake-weighted median is then $15. In this example, nearly half of committed stake is below $15, while a negligible amount of committed stake is above $15. Hence, even though the price submitted by validator 3 ($1000) is a significant outlier, it has no influence on the final calculation as long as its value is higher than $15.

A stake weighted median might not always be unique, two prices could satisfy the criteria of “best division.” In this case, Slinky always chooses the lower stake-weighted median i.e. the lowest price among the two. For instance, the median would be non-unique if validator 3 submitted a price between $10-$15. In this case they are able to change the median, but only within a range bounded by the prices submitted by neighboring central-weighted validators, so between $10-15.

This can be summarized in the table below:

Validator 1 Validator 2 Validator 3 Validator 4 S.W. Median
Stake 1/3 1/3 1/100 1/100 -
Block 1 price $10 $15 $1000 $15.1 $15
Block 2 price $10 $15 $15.2 $15.1 $15
Block 3 price $10 $15 $14.5 $15.1 $14.5
Block 4 price $10 $15 $5 $15.1 $10

Contrast this behavior with a stake-weighted average,

p = 10\times \frac{1}{3} + 15 \times \frac{1}{3} + 1000\times \frac{1}{100} + 15.1 \times \frac{1}{100} \approx \$18.5

where a validator with less than 2% of committed stake can cause a significant price deviation. A stake-weighted median has a known manipulation bound, which is typically much tighter.

The attack on Levana

Introducing the Oracle Supply Chain

Levana is a perpetual swap protocol built on Osmosis. The oracle prices used by the protocol are provided by Pyth, which is a transaction-based oracle. Pyth posts prices generated on a separate SVM-based chain, Pythnet, and prices must be relayed from Pythnet to Osmosis for use in protocols like Levana.Relaying is handled by the Wormhole network, a cross-chain messaging protocol with independent operators. On Pythnet itself, the prices are fetched and aggregated by a set of publishers. Like Slinky, price aggregation uses a stake-weighted median.

Relaying a price from Pyth to Levana consists of the following steps:

  • The Wormhole network listens to Pyth oracle updates on Pythnet.
  • When a new price is posted to Pythnet, Wormhole reaches agreement on the validity of the next price, producing an attestation proof.
  • The Pyth price, along with the attestation proof, is relayed by Wormhole and published to the Osmosis oracle contract.
  • The oracle contract on Osmosis verifies the attestation proof and updates the local price.
  • The price update is read from the Osmosis oracle contract into the market contract on Levana. This action necessitates either a trader opening a position on Levana, or the submission of a special transaction by Levana’s off-chain infrastructure.

The last two steps are permissionless. In the penultimate step anyone can submit an attestation proof to Osmosis thereby updating the oracle contract price. In the final step any trader can update the price on the Levana market contract by automatically opening a position.

When the Levana market contract is updated that price has a 2 minute lifetime, after which it becomes stale. This means that a trader cannot use a price older than 2 minutes to open a position. This two minute window will be important later when discussing the oracle attack that Levana experienced.

It’s also important to note that Levana has dedicated infrastructure and off-chain mechanisms to enforce regular price updates, specifically:

  • Levana’s off-chain update mechanism will regularly query the Osmosis oracle contract for the latest oracle update to refresh the market contract
  • Levana’s specialized off-chain bots publish price updates to the oracle contract whenever there is a significant discrepancy between the on-chain price and the Pythnet price

Breaking down the attack

Because price updates require the inclusion of a special transaction within the Osmosis chain, the attacker used a congestion attack (essentially spamming the chain with transactions) to prevent delivery of oracle updates.

At the time of attack Osmosis nodes were running an interim fee market which simulated the behavior of EIP-1559, however without in-protocol enforcement of the base fee.

By creating additional transaction load the attacker raised the gas price, thereby increasing the base fee. Subtle, yet consequential, interactions exist between fee controllers and the P2P layer, as Skip has previously noted, however for our purposes what’s important to note is elevated fees prevented both traders and the Levana team from updating the price.

With the attacker able to control network congestion, they could manipulate transaction flow in order to selectively deliver their own price update. By controlling the timing of price updates the attacker was able to open positions with advance knowledge of the closing price, allowing them to execute deterministically profitable strategies.

Detailed description of the attack

As a reminder, in derivatives trading a long position is a bet that the on-chain market price will rise, and a short position is a bet that the price will fall. Applying leverage magnifies the payoff, in exchange for incurring liquidation risk. Given a certain amount of capital, a levered bet allows a trader to increase their profits (or losses) while providing only a fraction of the position upfront and borrowing the remainder from the protocol. Such bets are structured through the on-chain perpetual futures contracts provided by Levana.

The Levana attack comprised a sequence of directional leveraged long and short positions that the attacker knew to be profitable. The attacker’s information advantage comes from their control of price updates included in the chain, allowing them to reliably exploit significant deviations in upstream oracle prices.

Attack timeline

Fundamentally, the attack sequence is as follows:

  1. Someone updates the oracle price
  2. The attacker opens a position
  3. The attacker updates the oracle price again
  4. The attacker closes their position

Each of these steps corresponds to a Cosmos SDK message. When the attacker detects a non-negligible discrepancy between two Pyth prices they execute the attack. What constitutes “non-negligible” is specific to the attacker, and depends on the applied leverage and available capital.

There are two attack scenarios that can be considered, one in which the 2 minute expiration time matters (scenario 1), and another in which it only constrains the latency of the attacker, i.e. their ability to open a position 2 minutes after they submit their oracle update (scenario 2). In the second scenario the attacker can leverage the fact that the 2 minute window uses the Osmosis clock, which may advance independently of new price updates posted to Pythnet.

The two scenarios are distinguished by whether the attacker decides to update the first oracle price themself, or let other parties do so for them. Since the Levana contract disallows updating the oracle twice within a single transaction, in the second scenario the attacker must precede the attack sequence with an oracle update transaction before atomically bundling the messages corresponding to the three last steps.

Let’s look at the timeline for scenario 1 and 2:

  1. The attacker waits for another actor to submit an oracle update to Osmosis, fixing the price in the oracle contract to e.g $10. This step only takes place in scenario 1.
  2. The attacker blocks all oracle updates from reaching the Levana market by creating congestion on Osmosis.
  3. The attacker monitors Osmosis for all oracle updates relayed from Wormhole within the 2 minute window initiated in step 1. In scenario 2, the first oracle update transaction, and thus clock initialization, begins at the attacker’s discretion.
  4. The attacker monitors Pythnet and notices a non-negligible deviation from $10, say an oracle update of $12. In scenario 2, the attacker notices a non-negligible deviation between two oracle updates on Pythnet, $10 and $12.
  5. Since the Pyth price increased from $10 to $12, the attacker opens a long position at $10, which will also update the market price to $10. The $10 oracle update can be pushed to the chain since it is below the 2 minute window. In scenario 2, the attacker must first submit the $10 oracle update to the oracle, then within the two minute timeout they submit another transaction to open the position for the attack (otherwise they can send another oracle update to reset the clock and begin again).
  6. The attacker publishes the $12 price update along with its attestation proof to the oracle contract.
  7. The attacker takes profit by closing their long position at $12.

note: steps 5, 6, and 7 can be executed as an atomic multi-message transaction

If the attacker opened a $100K position at 10x leverage in step 6 they could buy $1M worth of the underlying token. If we assume that the initial price was $10, this corresponds to an inventory of $100K tokens. When the attacker closes their position the price of the underlying token is $12, hence the attacker’s token inventory is sold for $1.2M, or at $200K profit.

Levana vulnerability space

The key vulnerability exploited during the attack is the following: the liveness of Levana markets is decoupled from disruptions in the timely delivery of new oracle prices.

Levana markets were not sufficiently aware of oracle disruption

The attacker disrupted the on-chain price update mechanism, preventing anyone from submitting an update transaction. However because attacker was able to selectively control congestion, they could manipulate traffic to submit their own update transaction at a time of their choosing. Consequently, they were able to effectively control the price update mechanism for 2 minute windows, and used their control to perform profitable trades.

The disruption of the price update mechanism was performed via a congestion attack targeting inclusion within the block, as well as a DoS attack on the Levana infrastructure responsible for publishing the attestation proofs and forcing price updates. These DoS attack vectors reflect two permissionless systems present within the oracle supply chain: the publication of the attestation proof, and the publication of the oracle price to Levana. While a permissionless mechanism was intended to improve the latency and robustness of price updates by allowing anyone to submit an oracle update transaction, systems with unrestricted access are fundamentally vulnerable to DoS attacks. The only mitigation is redundancy, work-based gating, or the re-introduction of permissioning.

Controlling price submission is enough for a profitable attack

It is important to note that the price itself was not manipulable by the hacker, as the update consisted of signed Pyth data. However, the attacker was able to manipulate the timing of update submission, which was sufficient to extract profit from the protocol. This manipulation was carried out through a congestion attack which had the following effect:

  • increased the cost of others’ submitting oracle updates
  • prevented the dissemination of transactions due to idiosyncrasies in the interaction of the CometBFT P2P layer and the mempool

This was compounded by challenges users faced with accurate fee estimation resulting from recent modifications to Osmosis’ fee market.

Further vulnerabilities

Fragmenting security across multiple off-chain parties creates a larger attack surface

Fundamentally, the attack was possible due to critical parts of the oracle supply chain remaining outside of the protocol’s control, specifically the production and submission of prices.

Price production

For price production, the protocol trusts:

  • The price publishers that post prices on Pythnet
  • The Pythnet consensus mechanism that produces the price
  • The Wormhole committee that attests to the price produced by Pyth

These trust assumptions were not broken. Neither Pyth nor Wormhole were compromised, however the mere fact that they occur outside of consensus is sufficient to expose the protocol to exploit.

Price submission

For price submission, the protocol trusts:

  • The Wormhole relayers, Levana’s off-chain bots, or traders to submit oracle prices in a timely manner
  • Osmosis to always maintain conditions where prices can be permissionlessly submitted

The attacker was able to compromise these trust assumptions in their exploit.

Because the Pyth oracle, Wormhole committee, relayers submitting prices, and the Osmosis validators are all effectively independent security domains, the security of the oracle reduces to the weakest link along the supply chain. The attacker only needs to target the infrastructure they believe to be the most vulnerable in order to interfere with the price update mechanism.

Monopolistic proposers are susceptible to vulnerability

The proposer’s discretion over block production impacts oracle transaction inclusion. This means that the proposer can be attacked (as in the Levana exploit), or that the proposer can act adversarially.

Exogenous attack on the proposer

Every round of consensus only one validator must be targeted to affect the inclusion of an oracle update. This is easily accomplished by spamming the proposer with transactions. Such a spam attack can have the following effect:

  • Filling the proposer’s block
  • Disrupting the proposer’s gossip network
  • Raising gas prices significantly

Each of these results may deny oracle update transactions from the proposer’s block.

Adversarial proposer

Alternatively, if the proposer is adversarial, they have complete control of oracle inclusion at that block height due to their temporary monopoly on the block production. They can delay inclusion of an oracle update for the duration of that block. While there is no evidence of proposer misbehavior in the recent Levana attack, it is worth noting a well functioning oracle relies on all proposers acting honestly.

Without an attributable supply chain the protocol cannot punish critical actor deviation

Thus far we’ve discussed where possible attacks may occur. However, in the event of an attack it’s also important to understand who is responsible so that they may be held accountable. In the supply chain described above offenses cannot be uniquely attributed to a specific party, therefore they cannot be held responsible. For example, the proposer could have intentionally slowed or excluded an oracle update that a Wormhole relayer submitted to the chain. However, it’s also possible that the proposer may not have received the oracle update in the first place. The protocol has no way to distinguish these two scenarios and therefore cannot attribute fault.

During the Levana attack the mempool was flooded with transactions. This was likely caused by an off-chain party, however if a validator hypothetically wanted to perform the attack they could do so with plausible deniability, for example by claiming DoS prevented them from seeing the update transaction.

Levana’s subsequent mitigations introduce trade-offs

Subsequent to the attack, Levana proposed to mitigate oracle manipulation by decoupling the placement and execution of orders through a queue system. In this updated design, new orders cannot be executed until a new oracle update is registered by the protocol. The drawback, however, is the queue system degrades protocol liveliness and after placing an order users must wait for an oracle update to register. Furthermore, the queue system introduces the complexity of handling time-outs, if an oracle update fails to arrive in time all pending user transactions will be cancelled.

Slinky is robust to oracle attacks up to 1/3 corruption of stake

Slinky inherits the security of the underlying chain

Slinky mitigates the DoS vulnerability that Levana suffered entirely by bringing portions of the oracle supply chain that were off-chain into the consensus process. This gives application developers the assurance that application liveness requires the oracle to operate correctly. That is, the chain will halt if:

  1. The oracle is not updating per-block
  2. The oracle is reporting incorrect prices
  3. Less than \frac{2}{3} of all validator stake was committed to oracle price production

This prevents taking advantage of price staleness, thus eliminating the threat posed by the Levana attacker. Using Slinky the application simply cannot progress without a price update, thus preventing attackers from gaining an information advantage to place malicious trades.

Concretely, corrupting Slinky’s security would require corruption of the consensus mechanism itself, either by controlling or disabling more than \frac{1}{3} of stake. Let’s look at the lengths an adversary would need to go in order to perform an attack on the Slinky system.

1. Controlling more than \frac{1}{3} of stake power

In a stake-weighted median, if one of the submitted prices has an associated stake greater than \frac{1}{2} of the total committed stake it will be selected as the median price. In this example we have four validators.

We can see that Validator 1 submitted the lowest price, $1. All other validators comprising \frac{1}{6} + \frac{1}{12} + \frac{1}{12} = \frac{1}{3} of stake, or half of the total committed stake, submitted higher prices. However, in this example Validator 1 was able to entirely determine the price, the result of the stake-weighted median is $1.

Under the circumstance of exactly \frac{2}{3} of committed stake (as in the above example), possessing \frac{1}{3} of the total stake is enough to manipulate the oracle price. However, an attacker would need to control at least \frac{1}{2} of total stake to manipulate the oracle price with certainty. One should also note that Slinky can be parameterized to require greater than \frac{2}{3} of total stake in order to product a aggregate price, trading off liveness for this additional safety assurance.

2. Attacking a set of validators accounting for more than \frac{1}{3} of total stake

In order to prevent Slinky from producing an oracle price the attacker would need to eclipse validators accounting for more than \frac{1}{3} of total stake, preventing them from publishing their vote extensions to the network. By pigeonhole principle, all information transmitted by any validator who is part of any quorum accounting for \frac{2}{3} or more of total stake, will be incorporated into the consensus process.

Slinky provides the attributability required for oracle soundness

Contrary to an external oracle configuration, Slinky’s enshrined functions ensure that offenses related to oracle production are slashable. Because queried prices are posted within validators’ signed vote extensions, the protocol can definitively slash the offending validator’s stake.

Furthermore, Slinky eliminates proposer discretion over the production or submission of an oracle price, within tight, verifiable bounds. All valid blocks must include an oracle update and the price calculation must include at least \frac{2}{3} of the validator-posted Vote Extensions. Price aggregation then occurs in-protocol, which ensures that price aggregation is performed correctly, otherwise validators reject the proposal entirely and the protocol advances to the next round, selecting a new proposer to submit the expected proposal at the same block height.

Conclusion

In this post we described how an attacker can disrupt an asynchronously updated price oracle. In such cases the attacker has a clearly defined target, the oracle update transaction, with a large vulnerability surface to disrupt submission. The recent Levana incident on Osmosis illustrates such an exploit. In this case, although the attacker is only able to adversarially control the timing of oracle message submission, they are still able to extract significant value from the protocol. Though measures to mitigate such attacks are possible without modifications to the consensus layer, they introduce complexity and sacrifice user experience.

In contrast, we demonstrated how Slinky, an enshrined oracle using Vote Extensions, eliminates these attack vectors. Slinky provides oracle security that is uniform with consensus, the highest level of performance, and an optimal user experience. We encourage anyone interested in learning more about the Slinky implementation, or how to deploy it to their Cosmos SDK chain to reach out.

1 Like