We expect an oracle will publish some number 𝑁
by signing each of its n
bits.
Given a constant c
, we want to express the spending condition 𝑁 ≥ c
in a single adaptor point.
The key idea is to construct an OR operator for adaptor points. This is possible with verifiable encryption. An OR operator allows to condense complex spending conditions into a single point. This prevents the combinatorical explosions that usually occure when using multi-oracles. An OR operator makes spending conditions easily composable. In theory, it even enables arbitrary computations.
We define B₁
to Bₙ
to represent the adaptor points for oracle signatures of those bits of 𝑁
that are equal to 1
:
B₁ → "bit 1 is 1"
B₂ → "bit 2 is 1"
B₃ → "bit 3 is 1"
...
Bₙ → "bit n is 1"
For example, if 𝑁 = 9 = 0b00001001
then the oracle will publish the dlogs of B₁
and B₄
because only bit 1 and bit 4 are signed to be 1
. All other bits are 0
. This bitwise encoding of 𝑁
is helpful to implement our order relation.
Let's say c = 19 = 0b00010001
. We use multiple combinations of points Bᵢ
to express all potential values of 𝑁
that are greater than c
:
T₁ = B₅ + B₁ → "N is 00010001 or even more bits are 1"
T₂ = B₅ + B₂ → "N is 00010010 or even more bits are 1"
T₃ = B₅ + B₃ → "N is 00010100 or even more bits are 1"
T₄ = B₅ + B₄ → "N is 00011000 or even more bits are 1"
T₅ = B₆ → "N is 00100000 or even more bits are 1"
T₆ = B₇ → "N is 01000000 or even more bits are 1"
T₇ = B₈ → "N is 10000000 or even more bits are 1"
If you learn the dlog of T₁
, or T₂
, ..., or T₇
, then that implies the oracle signed a 𝑁 ≥ c
.
Alice and Bob can use these points in 2-of-2 adaptor signatures with public key tweaking such that only a single single-sig transaction hits the chain. For every point Tᵢ
Alice signs an adaptor signature for transaction m
:
sᵢ' = rᵢ + H(Rᵢ+Tᵢ|P|m) * x
This ensures, if Bob learns any Tᵢ
he can complete one of Alice's signatures for the transaction.
In general, to express "greater than" for n
-bit numbers, users have to compute about n
adaptor points Tᵢ
. (Exactly one adaptor point for each zero bit + one point for the number itself.)
Sidenote: The "less than" relation, 𝑁 ≤ c
, can be expressed simply by applying the same algorithm to the zero bits instead of the ones.
Our order relation would be much more handy if we could condense the n
many points into a single one. In other words, we'd like to have a logical OR operator for adaptor points:
If Alice learns the dlog of T₁
, or T₂
, ..., or Tₙ
, then she also learns the dlog of Q
.
This is equivalent to "If 𝑁 ≥ c
then Alice learns the dlog of Q
". Alice and Bob can agree on such a Q
by using verifiable encryption as described here.
- Bob chooses a random secret
q
and sends the corresponding pointQ=qG
to Alice. - For each
Tᵢ
:- Bob encrypts
q
usingTᵢ
as public key. He sends the ciphertext to Alice. - Bob also proves that the ciphertext is indeed the dlog of
q
encrypted withTᵢ
.
- Bob encrypts
- Alice verifies the proofs for all ciphertexts to ensure she learns
q
if he learns the dlog of anyTᵢ
.
This condenses all points Tᵢ
into a single point Q
.
Alice and Bob usually want to use multiple oracles to minimise trust. For each oracleᵢ they compute a condensed point Qᵢ
representing "Oracleᵢ signed 𝑁 ≥ c
".
Sums of such points, e.g. Qᵢ + Qⱼ + Qₖ
, represent that those oracles all agree on 𝑁 ≥ c
.
We can form a non-interactive t-of-n threshold point by enumerating all t-of-t subsets. For example, for a 2-of-3 we compute all sums of 2 oracles:
U₁ = Q₁ + Q₂
U₂ = Q₁ + Q₃
U₃ = Q₂ + Q₃
Again, we can condense the points U₁, U₂, U₃
to encode every possible outcome of the multi-oracle in a single point.
A 6-of-10 oracle requires to compute 210 points Uᵢ
for all combinations. That is still practical. Unfortunately, enumerating subsets doesn't scale much beyond that. However, for much larger thresholds we could split the oracles up into smaller threshold groups and then condense their points to recursively combine them into one larger threshold group.
Now, we can encode large multi-oracles in a single adaptor point.
Suppose our oracle signs only two bits. As shown below, we can construct a NAND gate from the four adaptor points. NAND is a universal gate and thus, DLCs can compute any Boolean function. Furthermore, it is possible to perform complex computations efficiently by condensing the adaptor points recursively - as described above. Thus, we can express payment conditions that perform arbitrary computations on oracle values.
For example, oracle1 signs the BTC/USD price and oracle2 signs USD/EUR. Using those values a DLC can compute a price for BTC/EUR.
Our oracle signs two bits: bit "A" and bit "B". So, we have 4 adaptor points A0, A1
and B0, B1
. We can express a NAND like:
C₁ = A0 + B0 → "result is 1"
C₂ = A0 + B1 → "result is 1"
C₃ = A1 + B0 → "result is 1"
C₄ = A1 + B1 → "result is 0"
We map the two possible outcomes 0
and 1
to two points D0
and D1
:
D0 = C₄ → "result is 0"
D1 = condense(C₁, C₂, C₃) → "result is 1"
This gives us composable NAND gates. They can express any Boolean function in a DLC.
A somewhat crazy idea is to use bitcoin as time oracle and validate a block header in a DLC.