Skip to content

Commit

Permalink
Merge pull request #77 from gnosis/doc-updates
Browse files Browse the repository at this point in the history
Documentation updates
  • Loading branch information
cag committed Sep 24, 2019
2 parents 4afa2fe + bcfd1ee commit 62e9803
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 53 deletions.
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ test/python/
requirements.txt
.travis.yml

# Documentation build #
#######################
docs/_build/

# Coverage stuff #
##################

Expand Down
131 changes: 78 additions & 53 deletions docs/developer-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ The condition ID may be determined off-chain from the parameters via ``web3``:
v: 3
})
A helper function for determining the condition ID also exists on the contract:
A helper function for determining the condition ID also exists on both the contract and the ``CTHelpers`` library:

.. autosolfunction:: ConditionalTokens.getConditionId

Expand Down Expand Up @@ -157,47 +157,84 @@ Index Set Representation and Identifier Derivation

A outcome collection may be represented by an a condition and an *index set*. This is a 256 bit array which denotes which outcome slots are present in a outcome collection. For example, the value ``3 == 0b011`` corresponds to the outcome collection ``(A|B)``, whereas the value ``4 == 0b100`` corresponds to ``(C)``. Note that the indices start at the lowest bit in a ``uint``.

A outcome collection may be identified with a 32 byte value called a *collection identifier*. In order to calculate the collection ID for ``(A|B)``, simply hash the condition ID and the index set:
A outcome collection may be identified with a 32 byte value called a *collection identifier*. Calculating the collection ID for an outcome collection involves hashing its condition ID and index set into a point on the `alt_bn128`_ elliptic curve.

.. code-block:: js
.. note::

web3.utils.soliditySha3({
// See section "A Categorical Example" for derivation of this condition ID
t: 'bytes32',
v: '0x67eb23e8932765c1d7a094838c928476df8c50d1d3898f278ef1fb2a62afab63'
}, {
t: 'uint',
v: 0b011 // Binary Number literals supported in newer versions of JavaScript
})
In order to calculate the collection ID for ``(A|B)``, the following steps must be performed.

This results in a collection ID of ``0x52ff54f0f5616e34a2d4f56fb68ab4cc636bf0d92111de74d1ec99040a8da118``.
1. An initial value for the point x-coordinate is set by hashing the condition ID and the index set of the outcome collection, and interpreting the resulting hash as a big-endian integer.

We may also combine collection IDs for outcome collections for different conditions by adding their values modulo 2^256 (equivalently, by adding their values and then taking the lowest 256 bits).
.. code-block:: js
.. note:: This truncated sum is used to combine collection IDs because it is commutative. Bitwise XOR is not used in this scenario because the operation makes the root collection ID ``bytes32(0)`` accessible by XOR-ing combining collection IDs with themselves.
web3.utils.soliditySha3({
// See section "A Categorical Example" for derivation of this condition ID
t: 'bytes32',
v: '0x67eb23e8932765c1d7a094838c928476df8c50d1d3898f278ef1fb2a62afab63'
}, {
t: 'uint',
v: 0b011 // Binary Number literals supported in newer versions of JavaScript
})
To illustrate, let's denote the slots for range ends 0 and 1000 from our scalar condition example as ``LO`` and ``HI``. We can find the collection ID for ``(LO)`` to be ``0xd79c1d3f71f6c9d998353ba2a848e596f0c6c1a9f6fa633f2c9ec65aaa097cdc``.
This results in an initial x-coordinate of ``0x52ff54f0f5616e34a2d4f56fb68ab4cc636bf0d92111de74d1ec99040a8da118``, or ``37540785828268254412066351790903087640191294994197155621611396915481249947928``.

The combined collection ID for ``(A|B)&(LO)`` can be calculated via:
An ``odd`` flag is set according to whether the highest bit of the hash result is set. In this case, because the highest bit of the hash result is not set,``odd = false``.

.. code-block:: js
2. The x-coordinate gets incremented by one modulo the order of the `alt_bn128`_ base field, which is ``21888242871839275222246405745257275088696311157297823662689037894645226208583``.

The first time, this results in an updated x-coordinate ``x = 15652542956428979189819946045645812551494983836899331958922359020836023739346``.

3. The x-coordinate is checked to see if it is the x-coordinate of points on the elliptic curve. Specifically, ``x**3 + 3`` gets computed in the base field, and if the result is a quadratic residue, the x-coordinate belongs to a pair of points on the elliptic curve. If the result is a non-residue however, return to step 2.

When ``x = 15652542956428979189819946045645812551494983836899331958922359020836023739346``, ``x**3 + 3 == 7181824697751204416624405172148440000524665091599802536460745194285959874882`` is not a quadratic residue in the base field, so go back to step 2.

When ``x = 15652542956428979189819946045645812551494983836899331958922359020836023739347``, ``x**3 + 3 == 19234863727839675005817902755221636205208068129817953505352549927470359854418`` is also not a quadratic residue in the base field, so go back to step 2.

When ``x = 15652542956428979189819946045645812551494983836899331958922359020836023739348``, ``x**3 + 3 == 15761946137305644622699047885883332275379818402942977914333319312444771227121`` is still not a quadratic residue in the base field, so go back to step 2.

When ``x = 15652542956428979189819946045645812551494983836899331958922359020836023739349``, ``x**3 + 3 == 18651314797988388489514246309390803299736227068272699426092091243854420201580`` is a quadratic residue in the base field, so we have found a pair of points on the curve, and we may continue.

4. Note that the base field occupies 254 bits of space, meaning the x-coordinate we found also occupies 254 bits of space, and has two free bits in an EVM word (256 bits). Leave the highest bit unset, and set the next highest bit if ``odd == true``. In our example, ``odd`` is unset, so we're done, and the collection ID for ``(A|B)`` is ``15652542956428979189819946045645812551494983836899331958922359020836023739349``, or ``0x229b067e142fce0aea84afb935095c6ecbea8647b8a013e795cc0ced3210a3d5``.

We may also combine collection IDs for outcome collections for different conditions by performing elliptic curve point addition on them.

.. note::

Let's denote the slots for range ends 0 and 1000 from our scalar condition example as ``LO`` and ``HI``. We can find the collection ID for ``(LO)`` to be ``0x560ae373ed304932b6f424c8a243842092c117645533390a3c1c95ff481587c2`` using the procedure illustrated in the previous note.

The combined collection ID for ``(A|B)&(LO)`` can be calculated in the following manner:

1. Decompress the constituent collection IDs into elliptic curve point coordinates. Take the low 254 bits as the x-coordinate, and pick the y-coordinate which is even or odd depending on the value of the second highest bit.

* ``(A|B)``, which has a collection ID of ``0x229b067e142fce0aea84afb935095c6ecbea8647b8a013e795cc0ced3210a3d5``, gets decompressed to the point::

(15652542956428979189819946045645812551494983836899331958922359020836023739349,
11459896044816691076313215195950563425899182565928550352639564868174527712586)

Note the even y-coordinate is chosen here.

* ``(LO)``, which has a collection ID of ``0x560ae373ed304932b6f424c8a243842092c117645533390a3c1c95ff481587c2``, gets decompressed to the point::

(9970120961273109372766525305441055537695652051815636823675568206550524069826,
5871835597783351455285190273403665696556137392019654883787357811704360229175)

The odd y-coordinate indication bit was chopped off the compressed form before its use as the decompressed form's x-coordinate, and the odd y-coordinate is chosen here.

2. Perform point addition on the `alt_bn128`_ curve with these points. The sum of these points is the point::

'0x' + BigInt.asUintN(256,
0x52ff54f0f5616e34a2d4f56fb68ab4cc636bf0d92111de74d1ec99040a8da118n +
0xd79c1d3f71f6c9d998353ba2a848e596f0c6c1a9f6fa633f2c9ec65aaa097cdcn
).toString(16)
(21460418698095194776649446887647175906168566678584695492252634897075584178441,
4596536621806896659272941037410436605631447622293229168614769592376282983323)

.. note:: `BigInt`_ is used here for the calculation, though `BN.js`_ or `BigNumber.js`_ should both also suffice.
3. Compress the result by taking the x-coordinate, and setting the second highest bit, which should be just outside the x-coordinate, depending on whether the y-coordinate was odd. The combined collection ID for ``(A|B)&(LO)`` is ``0x6f722aa250221af2eba9868fc9d7d43994794177dd6fa7766e3e72ba3c111909``.

This calculation yields the value ``0x2a9b72306758380e3b0a31125ed39a635432b283180c41b3fe8b5f5eb4971df4``.
.. warning:: Both bitwise XOR and truncated addition is not used in this scenario because these operations are vulnerable to collisions via `a generalized birthday attack`_.

Similar to with conditions, the contract also provides a helper function for calculating outcome collection IDs:
Similar to with conditions, the contract and the ``CTHelpers`` library also provide helper functions for calculating outcome collection IDs:

.. autosolfunction:: ConditionalTokens.getCollectionId

.. _BigInt: https://tc39.github.io/proposal-bigint/
.. _BN.js: https://github.com/indutny/bn.js/
.. _BigNumber.js: https://github.com/MikeMcl/bignumber.js/
.. _alt_bn128: https://eips.ethereum.org/EIPS/eip-196
.. _a generalized birthday attack: https://link.springer.com/chapter/10.1007/3-540-45708-9_19


Defining Positions
Expand All @@ -220,14 +257,14 @@ We may calculate the position ID for the position ``$:(A|B)`` via:
v: '0xD011ad011ad011AD011ad011Ad011Ad011Ad011A'
}, {
t: 'bytes32',
v: '0x52ff54f0f5616e34a2d4f56fb68ab4cc636bf0d92111de74d1ec99040a8da118'
v: '0x229b067e142fce0aea84afb935095c6ecbea8647b8a013e795cc0ced3210a3d5'
})
The ID for ``$:(A|B)`` turns out to be ``0x6147e75d1048cea497aeee64d1a4777e286764ded497e545e88efc165c9fc4f0``.
The ID for ``$:(A|B)`` turns out to be ``0x5355fd8106a08b14aedf99935210b2c22a7f92abaf8bb00b60fcece1032436b7``.

Similarly, the ID for ``$:(LO)`` can be found to be ``0xfdad82d898904026ae6c01a5800c0a8ee9ada7e7862f9bb6428b6f81e06f53bb``, and ``$:(A|B)&(LO)`` has an ID of ``0xcc77e750b61d29e158aa3193faa3673b2686ba9f6a16f51b5cdbea2a4f694be0``.
Similarly, the ID for ``$:(LO)`` can be found to be ``0x1958e759291b2bde460cdf2158dea8d0f5c4e22c77ecd09d3ca6a36f01616e02``, and ``$:(A|B)&(LO)`` has an ID of ``0x994b964b94eb15148726de8caa08cac559ec51a90fcbc9cc19aadfdc809f34c9``.

A helper function for calculating positions also exists:
Helper functions for calculating positions also exist:

.. autosolfunction:: ConditionalTokens.getPositionId

Expand Down Expand Up @@ -293,13 +330,9 @@ Collateral ``$`` can be split into conditional tokens in positions ``$:(A)``, ``
The effect of this transaction is to transfer ``amount`` DollaCoin from the message sender to the ``conditionalTokens`` to hold, and to mint ``amount`` of conditional token for the following positions:

========= ======================================================================
Symbol Position ID
========= ======================================================================
``$:(A)`` ``0x8c12fa3bb72c9c455acd4d6034989ec0ce9188afd7c89c8c42d064ed7fe5a9d8``
``$:(B)`` ``0x21aec03d8dfd8b5f0a2750718fe491e439f3625816e383b66a05cabd56624b4c``
``$:(C)`` ``0x8085f7c500098412ff2fc701a74174527e7b39a2b923cd0bca6ad2d5f7fa348d``
========= ======================================================================
* ``$:(A)``
* ``$:(B)``
* ``$:(C)``

.. note:: The previous example, where collateral was split into shallow positions containing collections with one slot each, is similar to ``Event.buyAllOutcomes`` from v1.

Expand All @@ -318,12 +351,8 @@ The set of ``(A)``, ``(B)``, and ``(C)`` is not the only nontrivial partition of
This transaction also transfers ``amount`` DollaCoin from the message sender to the ``conditionalTokens`` to hold, but it mints ``amount`` of conditional token for the following positions instead:

=========== ======================================================================
Symbol Position ID
=========== ======================================================================
``$:(B)`` ``0x21aec03d8dfd8b5f0a2750718fe491e439f3625816e383b66a05cabd56624b4c``
``$:(A|C)`` ``0xb33b3d0035913315b76e85842f682920f78b32c43c7175768c4c67e3f31e6413``
=========== ======================================================================
* ``$:(B)``
* ``$:(A|C)``

.. warning:: If non-disjoint index sets are supplied to ``splitPosition``, the transaction will revert.

Expand All @@ -343,7 +372,7 @@ It's also possible to split from a position, burning conditional tokens in that
// Here, instead of just supplying 32 zero bytes, we supply
// the collection ID for (A|B).
// This is NOT the position ID for $:(A|B)!
'0x52ff54f0f5616e34a2d4f56fb68ab4cc636bf0d92111de74d1ec99040a8da118',
'0x229b067e142fce0aea84afb935095c6ecbea8647b8a013e795cc0ced3210a3d5',
// This is the condition ID for the example scalar condition
'0x3bdb7de3d0860745c0cac9c1dcc8e0d9cb7d33e6a899c2c298343ccedf1d66cf',
// This is the only partition that makes sense
Expand All @@ -352,14 +381,10 @@ It's also possible to split from a position, burning conditional tokens in that
amount,
)
This transaction burns ``amount`` of conditional token in position ``$:(A|B)`` (position ID ``0x6147e75d1048cea497aeee64d1a4777e286764ded497e545e88efc165c9fc4f0``) in order to mint ``amount`` of conditional token in the following positions:
This transaction burns ``amount`` of conditional token in position ``$:(A|B)`` (position ID ``0x5355fd8106a08b14aedf99935210b2c22a7f92abaf8bb00b60fcece1032436b7``) in order to mint ``amount`` of conditional token in the following positions:

================ ======================================================================
Symbol Position ID
================ ======================================================================
``$:(A|B)&(LO)`` ``0xcc77e750b61d29e158aa3193faa3673b2686ba9f6a16f51b5cdbea2a4f694be0``
``$:(A|B)&(HI)`` ``0xbacf3ddf0474d567cd254ea0674fe52ab20a3e2ebca00ec71a846f3c48c5de9d``
================ ======================================================================
* ``$:(A|B)&(LO)``
* ``$:(A|B)&(HI)``

Because the collection ID for ``(A|B)&(LO)`` is just the sum of the collection IDs for ``(A|B)`` and ``(LO)``, we could have split from ``(LO)`` to get ``(A|B)&(LO)`` and ``(C)&(LO)``:

Expand All @@ -370,7 +395,7 @@ Because the collection ID for ``(A|B)&(LO)`` is just the sum of the collection I
// The collection ID for (LO).
// This collection contains an outcome collection from the example scalar condition
// instead of from the example categorical condition.
'0xd79c1d3f71f6c9d998353ba2a848e596f0c6c1a9f6fa633f2c9ec65aaa097cdc',
'0x560ae373ed304932b6f424c8a243842092c117645533390a3c1c95ff481587c2',
// This is the condition ID for the example categorical condition
// as opposed to the example scalar condition.
'0x67eb23e8932765c1d7a094838c928476df8c50d1d3898f278ef1fb2a62afab63',
Expand Down

0 comments on commit 62e9803

Please sign in to comment.