Making oracles for Uqbar on Urbit
This plan is adapted from Chainlink OCR described here: https://research.chain.link/ocr.pdf
Oracle contract is simple: it takes in a result value, and a bundle of attestations. The contract checks these signatures and updates the stored information only if the signatures are all valid, recent enough (some number of ETH blocks) and the total reaches the consensus threshold for this data feed. Defined inside the contract is some formula for taking many data observations and averaging them into a final value. This formula can be a simple median, or have mechanisms for removing outliers, etc. The contract provides a scry path for the stored information. This scry path contains options regarding how recent the last update to the stored information must have been, with recency measured in ETH block height.
With this contract deployed on Uqbar, an off-chain coordination protocol is required to generate these "attestation bundles". The attestations should be limited to planets and up, in order to enforce unique identities and "skin in the game".
The off-chain coordination protocol is as follows:
- Participating ships identify a data feed they wish to contribute to. They source data from some location and circulate a "vote" to other ships known to be participating in the same data feed. The "vote" structure looks something like this:
[=sig [%oracle-vote vote=* contract-id=@ux eth-block-height=@ud eth-block-hash=@ux]]
sig
is a signed hash of everything that follows. The shape of info
is defined by the contract for this data feed.
- As soon as a ship has seen enough votes that are recent enough, which can include its own vote, that ship creates a final attestation. The ship generates a transaction and posts the attestation to the data feed contract. The transaction type looks something like this:
[%oracle-attestation votes=(list [=ship =oracle-vote])]
Where oracle-vote
is the data structure circulated in step 1.
Notes
Ships which participate maliciously at any phase of the process (submitting unacceptably bad information, sharing invalid signatures, flooding other participants) can easily be blacklisted by other ships who observe this behavior. Since Urbit IDs provide sybil resistance, this allows the oracle network to reliably prune bad actors.
At the moment, we don't have the ability to charge tokens for a contract read (scry). This can be added to the execution engine*. These fees will be distributed to the ships who generated the information with a bonus reward going to the ship which submits the final attestation bundle, to cover gas fees.
The reward for oracles should be paid in the native Uqbar gas token. This allows the oracle system to (a) not require contrived tokenomics to reward participants, (b) adds convenience for saving and spending rewards, and (c) bolsters demand for the native token. This also makes it feasible to build the charge for reading a data feed directly into the execution engine, much how gas is paid.
*It might make a lot of sense to build read-fees at this level. Basically, a contract read path can list a fee in the native gas token, and the execution engine can add this to the gas fee accumulated by a transaction as it's being run. This allows any contract to optionally assign a cost to a read, and any transaction which includes one of these reads will pay into the native token account held by that contract. The oracle contract can easily then distribute these accumulated fees in any number of ways. Other contracts that don't include oracle data may also find uses for a fee-gated read path.