{"action":"created","comment":{"url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/comments/56802751","id":56802751,"diff_hunk":"@@ -0,0 +1,437 @@\n+import base64\n+import hashlib\n+from math import ceil\n+\n+import binascii\n+\n+from six import string_types\n+\n+MSB = 0x80\n+REST = 0x7F\n+MSBALL = ~REST\n+INT = 2 ** 31\n+\n+# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER\n+# we don't use sys.maxint (= 2 ** 63 - 1) as this spec is inline with the ILP JavaScript reference implementation\n+# see https://interledger.org/\n+MAX_SAFE_INTEGER_JS = 2 ** 53 - 1\n+\n+\n+class UnsignedLEB128:\n+    \"\"\"\n+    Adapter for DWARF unsigned LEB128.\n+\n+    A VARUINT is a variable length integer encoded as base128 where the highest\n+    bit indicates that another byte is following. The first byte contains the\n+    seven least significant bits of the number represented.\n+\n+    see: https://en.wikipedia.org/wiki/LEB128 (ULEB128)\n+    see: http://grokbase.com/t/python/python-list/112e5jpc16/encoding\n+\n+    \"\"\"\n+\n+    @staticmethod\n+    def encode(obj):\n+        out = []\n+        value = int(obj)\n+        while value > INT:\n+            out.append((value & REST) | MSB)\n+            value /= 7\n+\n+        while value & MSBALL:\n+            out.append((value & REST) | MSB)\n+            value >>= 7\n+\n+        out.append(value | 0)\n+        return out\n+\n+    @staticmethod\n+    def decode(obj):\n+        value = 0\n+        for b in reversed(obj):\n+            value = value * 128 + (ord(b) & 0x7F)\n+        return value\n+\n+\n+class Writer:\n+    def __init__(self):\n+        self.components = []\n+\n+    def write_var_uint(self, value):\n+        \"\"\"\n+        Write a VARUINT to the stream.\n+\n+        Args:\n+            value (int): Integer to represent.\n+        \"\"\"\n+        out = UnsignedLEB128.encode(value)\n+        self.write(out)\n+\n+    def write_var_bytes(self, buffer):\n+        \"\"\"\n+        Write a VARBYTES.\n+\n+        A VARBYTES field consists of a VARUINT followed by that many bytes.\n+\n+        Args:\n+            buffer (Buffer): Contents of the VARBYTES.\n+        \"\"\"\n+        self.write_var_uint(len(buffer))\n+        self.write(buffer)\n+\n+    def write(self, in_bytes):\n+        \"\"\"\n+        Write a series of raw bytes.\n+\n+        Adds the given bytes to the output buffer.\n+\n+        Args:\n+            in_bytes (Buffer): Bytes to write.\n+        \"\"\"\n+        out = in_bytes\n+        if isinstance(out, (list, bytearray)):\n+            out = binascii.unhexlify(''.join('{:02x}'.format(x) for x in out))\n+        if not isinstance(out, bytes):\n+            out = out.encode('utf-8')\n+        self.components.append(out)\n+\n+\n+class Hasher(Writer):\n+\n+    def __init__(self, algorithm):\n+        if algorithm == 'sha256':\n+            self.hash = hashlib.sha256()\n+        else:\n+            raise NotImplementedError\n+        super().__init__()\n+\n+    def write(self, in_bytes):\n+        \"\"\"\n+        Adds bytes to the hash input.\n+        \n+        The hasher will pass these bytes into the hashing function. By overriding\n+        the Writer class and implementing this method, the Hasher supports any of\n+        the datatypes that a Writer can write.\n+        \n+        Args:\n+            in_bytes (Buffer): Bytes to add to the hash.\n+        \"\"\"\n+        out = in_bytes\n+        if isinstance(out, (list, bytearray)):\n+            out = binascii.unhexlify(''.join('{:02x}'.format(x) for x in out))\n+        if not isinstance(out, bytes):\n+            out = out.encode('utf-8')\n+        self.hash.update(out)\n+\n+    def digest(self):\n+        \"\"\"\n+        Return the hash.\n+\n+        Returns the finished hash based on what has been written to the Hasher so far.\n+\n+        Return:\n+            Buffer: Resulting hash.\n+        \"\"\"\n+        return self.hash.digest()","path":"bigchaindb/crypto/buffer.py","position":135,"original_position":135,"commit_id":"9c0e9f62315cc22f2b50d09b510665e1e4f3d165","original_commit_id":"9c0e9f62315cc22f2b50d09b510665e1e4f3d165","user":{"login":"r-marques","id":1793187,"avatar_url":"https://avatars.githubusercontent.com/u/1793187?v=3","gravatar_id":"","url":"https://api.github.com/users/r-marques","html_url":"https://github.com/r-marques","followers_url":"https://api.github.com/users/r-marques/followers","following_url":"https://api.github.com/users/r-marques/following{/other_user}","gists_url":"https://api.github.com/users/r-marques/gists{/gist_id}","starred_url":"https://api.github.com/users/r-marques/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/r-marques/subscriptions","organizations_url":"https://api.github.com/users/r-marques/orgs","repos_url":"https://api.github.com/users/r-marques/repos","events_url":"https://api.github.com/users/r-marques/events{/privacy}","received_events_url":"https://api.github.com/users/r-marques/received_events","type":"User","site_admin":false},"body":"would it make sense to return the hex representation of the digest?","created_at":"2016-03-21T10:31:20Z","updated_at":"2016-03-21T10:31:20Z","html_url":"https://github.com/bigchaindb/bigchaindb/pull/146#discussion_r56802751","pull_request_url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146","_links":{"self":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/comments/56802751"},"html":{"href":"https://github.com/bigchaindb/bigchaindb/pull/146#discussion_r56802751"},"pull_request":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146"}}},"pull_request":{"url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146","id":63438532,"html_url":"https://github.com/bigchaindb/bigchaindb/pull/146","diff_url":"https://github.com/bigchaindb/bigchaindb/pull/146.diff","patch_url":"https://github.com/bigchaindb/bigchaindb/pull/146.patch","issue_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/146","number":146,"state":"open","locked":false,"title":"Crypto conditions following ILP spec (sha256, ed25519, threshold)","user":{"login":"diminator","id":6178597,"avatar_url":"https://avatars.githubusercontent.com/u/6178597?v=3","gravatar_id":"","url":"https://api.github.com/users/diminator","html_url":"https://github.com/diminator","followers_url":"https://api.github.com/users/diminator/followers","following_url":"https://api.github.com/users/diminator/following{/other_user}","gists_url":"https://api.github.com/users/diminator/gists{/gist_id}","starred_url":"https://api.github.com/users/diminator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/diminator/subscriptions","organizations_url":"https://api.github.com/users/diminator/orgs","repos_url":"https://api.github.com/users/diminator/repos","events_url":"https://api.github.com/users/diminator/events{/privacy}","received_events_url":"https://api.github.com/users/diminator/received_events","type":"User","site_admin":false},"body":"solves #127 \r\n\r\n- [x] conversion of reference ILP javascript library\r\n- [x] SHA256\r\n- [ ] RSA\r\n- [x] ED25519\r\n- [x] Threshold\r\n\r\n# Crypto Conditions\r\n\r\nThis spec is from the [**Interledger Protocol (ILP)**]\r\n(https://interledger.org/five-bells-condition/spec.html)\r\n\r\n## Motivation\r\n\r\nWe would like a way to describe a signed message such that multiple actors in a distributed system can all verify the same signed message and agree on whether it matches the description.\r\n\r\nThis provides a useful primitive for distributed, event-based systems since we can describe events (represented by signed messages) and therefore define generic authenticated event handlers.\r\n\r\n## Terminology\r\n\r\n* ##### Condition\r\n  A condition is the hash of a description of a signed message.\r\n\r\n* ##### Fulfillment\r\n  A fulfillment consists of a description of a signed message and a signed message that matches the description.\r\n\r\n  The description can be hashed and compared to a condition. If the message matches the description and the hash of the description matches the condition, we say that the fulfillment **fulfills** the condition.\r\n\r\n* ##### Hashlock\r\n  A tuple consisting of a bytestring and its hash where the hash is published first and the publication of the corresponding bytestring acts as a one-bit, one-time signature.\r\n\r\n# Basic Format\r\n\r\n## Bitmask\r\n\r\nAny system accepting crypto-conditions must be able to state its supported\r\nalgorithms. It must be possible to verify that all algorithms used in a certain\r\ncondition are indeed supported even if the fulfillment is not available yet.\r\n\r\nIn order to meet these design goals, we define a bitmask to express the supported primitives.\r\n\r\nThe following bits are assigned:\r\n\r\n|Type Bit |Exp.         |Int.|Condition Type   |\r\n|--------:|------------:|---:|-----------------|\r\n|        1|2<sup>0</sup>|   1|SHA-256          |\r\n|       10|2<sup>1</sup>|   2|RSA-SHA-256      |\r\n|      100|2<sup>2</sup>|   4|THRESHOLD-SHA-256|\r\n|     1000|2<sup>3</sup>|   8|ED25519-SHA-256  |\r\n\r\nConditions contain a bitmask of types they require the implementation to support. Implementations provide a bitmask of types they support.\r\n\r\n### ILP Features\r\n\r\nCrypto-conditions are a simple multi-algorithm, multi-message, multi-level, multi-signature standard.\r\n\r\n* **Multi-algorithm**\r\n\r\n  Crypto-conditions can support several different signature and hash algorithms and support for new ones can be added in the future.\r\n\r\n  Implementations can state their supported algorithms simply by providing a bitmask. It is easy to verify that a given implementation will be able to verify the fulfillment to a given condition, by verifying that the condition's bitmask `condition` and its own bitmask of supported algorithms `supported` satisfies `condition & ~supported == 0` where `&` is the bitwise AND operator and `~` is the bitwise NOT operator.\r\n\r\n  Any new high bit can redefine the meaning of any existing lower bits as long as it is set. This can be used to remove obsolete algorithms.\r\n\r\n  The bitmask is encoded as a varint to minimize space usage.\r\n\r\n* **Multi-signature**\r\n\r\n  Crypto-conditions can abstract away many of the details of multi-sign. When a party provides a condition, other parties can treat it opaquely and do not need to know about its internal structure. That allows parties to define arbitrary multi-signature setups without breaking compatibility.\r\n\r\n  Protocol designers can use crypto-conditions as a drop-in replacement for public key signature algorithms and add multi-signature support to their protocols without adding any additional complexity.\r\n\r\n* **Multi-level**\r\n\r\n  Basic multi-sign is single-level and does not support more complex trust relationships such as \"I trust Alice and Bob, but only when Candice also agrees\". In single level 2-of-3 Alice and Bob could sign on their own, without Candice's approval.\r\n\r\n  Crypto-conditions add that flexibility elegantly, by applying thresholds not just to signatures, but to conditions which can be signatures or further conditions. That allows the creation of an arbitrary threshold boolean circuit of signatures.\r\n\r\n* **Multi-message**\r\n\r\n  Crypto-conditions can sign not just one, but multiple messages at the same time and by different people. These messages can then be used as inputs for other algorithms.\r\n\r\n  This allows resource-controlling systems to perform their functions without knowing the details of the higher-level protocols that these functions are a part of.\r\n\r\n## Usage\r\n\r\n```python\r\nimport binascii\r\nfrom bigchaindb.crypto.condition import Condition\r\nfrom bigchaindb.crypto.fulfillment import Fulfillment\r\nfrom bigchaindb.crypto.fulfillments.sha256 import Sha256Fulfillment\r\n\r\n# Parse a condition from a URI\r\nexample_condition_uri = 'cc:1:1:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:1'\r\nparsed_condition = Condition.from_uri(example_condition_uri)\r\nprint(isinstance(parsed_condition, Condition))\r\n# prints True\r\n\r\nprint(binascii.hexlify(parsed_condition.hash))\r\n# prints b'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'\r\n\r\n# Compile a condition\r\nparsed_condition_uri = parsed_condition.serialize_uri()\r\nprint(parsed_condition_uri)\r\n# prints 'cc:1:1:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:1'\r\nprint(parsed_condition_uri == example_condition_uri)\r\n# prints True\r\n\r\n# Parse a fulfillment\r\nexample_fulfillment_uri = 'cf:1:1:AA'\r\nparsed_fulfillment = Fulfillment.from_uri(example_fulfillment_uri)\r\nprint(isinstance(parsed_fulfillment, Sha256Fulfillment))\r\n# prints True\r\n# Note: Merely parsing a fulfillment DOES NOT validate it.\r\n\r\n# Validate a fulfillment\r\nparsed_fulfillment.validate()\r\n# prints True\r\n```\r\n\r\n## ILP Encoding\r\n\r\n### Binary types\r\n\r\n* **VARUINT**\r\n\r\n  Unsigned variable-length integer. Implementation matches [Base128 Varints](https://developers.google.com/protocol-buffers/docs/encoding#varints) in Protocol Buffers. Implementations MAY define different maximum lengths for their varints, as long as that length is long enough to cover their bitmask and their maximum supported fulfillment length. (This is safe, because no larger varuint can appear in a valid crypto-condition.)\r\n\r\n* **VARBYTES**\r\n\r\n  Consists of a `VARUINT` length field followed by that many bytes.\r\n\r\n* **VARARRAY**\r\n\r\n  Consists of a `VARUINT` length fields followed by that many bytes filled with elements of the array.\r\n\r\n### String types\r\n\r\n* **BASE10**\r\n\r\n  Variable-length integer encoded as a base-10 (decimal) number. Implementations MUST reject encodings that are too large for them to parse. Implementations MUST be tested for overflows.\r\n\r\n* **BASE16**\r\n\r\n  Variable-length integer encoded as a base-16 (hexadecimal) number. Implementations MUST reject encodings that are too large for them to parse. Implementations MUST be tested for overflows. No leading zeros.\r\n\r\n* **BASE64URL**\r\n\r\n  Base64-URL encoding. See [RFC4648 Section 5](https://tools.ietf.org/html/rfc4648#section-5).\r\n\r\n### Condition\r\n\r\nConditions are ASCII encoded as:\r\n\r\n```\r\n\"cc:\" BASE10(VERSION) \":\" BASE16(TYPE_BITMASK) \":\" BASE64URL(HASH) \":\" BASE10(MAX_FULFILLMENT_LENGTH)\r\n```\r\n\r\nConditions are binary encoded as:\r\n\r\n```\r\nCONDITION =\r\n  VARUINT TYPE_BITMASK\r\n  VARBYTES HASH\r\n  VARUINT MAX_FULFILLMENT_LENGTH\r\n```\r\n\r\nThe `TYPE_BITMASK` is the boolean OR of the `TYPE_BIT`s of the condition type and all subcondition types, recursively.\r\n\r\n### Fulfillment\r\n\r\nFulfillments are ASCII encoded as:\r\n\r\n```\r\n\"cf:\" BASE10(VERSION) \":\" BASE16(TYPE_BIT) \":\" BASE64URL(FULFILLMENT_PAYLOAD)\r\n```\r\n\r\nFulfillments are binary encoded as:\r\n\r\n```\r\nFULFILLMENT =\r\n  VARUINT TYPE_BIT\r\n  FULFILLMENT_PAYLOAD\r\n```\r\n\r\nThe `TYPE_BIT` is the single bit representing the top level condition type.\r\n\r\n# Condition Types\r\n\r\n## SHA-256\r\n\r\nSHA-256 is assigned the type bit 2<sup>0</sup> = 0x01.\r\n\r\n### Notes\r\n\r\nThis type of condition is also called a hashlock. We can use revealing the preimage as a type of one bit signature.\r\n\r\nBitcoin supports this type of condition via the `OP_HASH256` operator\r\n\r\n### Condition\r\n\r\n```\r\nHASH = SHA256(PREIMAGE)\r\n```\r\n\r\n### Fulfillment\r\n\r\n```\r\nFULFILLMENT_PAYLOAD =\r\n  VARBYTES PREIMAGE\r\n```\r\n\r\n### Usage\r\n\r\n```python\r\nimport binascii, hashlib\r\nfrom bigchaindb.crypto.condition import Condition\r\nfrom bigchaindb.crypto.fulfillments.sha256 import Sha256Fulfillment\r\n\r\nsecret = ''\r\npuzzle = binascii.hexlify(hashlib.sha256(secret.encode()).digest())\r\nprint(puzzle)\r\n# prints b'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'\r\n\r\n# Create a SHA256 condition\r\nsha256condition = Condition()\r\nsha256condition.bitmask = 0x01\r\nsha256condition.hash = binascii.unhexlify(puzzle)\r\nsha256condition.max_fulfillment_length = 1\r\nsha256condition_uri = sha256condition.serialize_uri()\r\nprint(sha256condition_uri)\r\n# prints 'cc:1:1:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:1'\r\n\r\n# Create a fulfillment\r\nsha256fulfillment = Sha256Fulfillment()\r\n\r\n# Create a condition from fulfillment\r\nsha256fulfillment.condition\r\n# raises ValueError: Could not calculate hash, no preimage provided\r\nsha256fulfillment.preimage = secret\r\nprint(sha256fulfillment.condition.serialize_uri() == sha256condition_uri)\r\n# prints True\r\n\r\n# Compile a fulfillment\r\nprint(sha256fulfillment.serialize_uri())\r\n# prints 'cf:1:1:AA'\r\n\r\n# Even better: verify that the fulfillment matches the condition\r\nprint(sha256fulfillment.validate() and \\\r\n    sha256fulfillment.condition.serialize_uri() == sha256condition.serialize_uri())\r\n# prints True\r\n```\r\n\r\n## RSA-SHA-256\r\n\r\nRSA-SHA-256 is assigned the type bit 2<sup>1</sup> = 0x02.\r\n\r\n**Warning:** not (yet) implemented in BigchainDB, for info see the [**ILP specification**](https://interledger.org/five-bells-condition/spec.html)\r\n\r\n\r\n## ED25519-SHA-256\r\n\r\nED25519-SHA-256 is assigned the type bit 2<sup>3</sup> = 0x08.\r\n\r\n### Condition\r\n\r\n```\r\nHASH = SHA256(\r\n  VARBYTES PUBLIC_KEY\r\n  VARBYTES MESSAGE_ID\r\n  VARBYTES FIXED_PREFIX\r\n  VARUINT DYNAMIC_MESSAGE_LENGTH\r\n)\r\n```\r\n\r\n### Fulfillment\r\n\r\n```\r\nFULFILLMENT_PAYLOAD =\r\n  VARBYTES PUBLIC_KEY\r\n  VARBYTES MESSAGE_ID\r\n  VARBYTES FIXED_PREFIX\r\n  VARUINT DYNAMIC_MESSAGE_LENGTH\r\n  VARBYTES DYNAMIC_MESSAGE\r\n  VARBYTES SIGNATURE\r\n```\r\n\r\nThe `DYNAMIC_MESSAGE_LENGTH` is included to provide a maximum length for `DYNAMIC_MESSAGE` even if the actual message suffix length is different. This value is used to calculate the `MAX_FULFILLMENT_LENGTH` in the condition.\r\n\r\nThe `MESSAGE_ID` represents an identifier for the message. All messages in a cryptocondition that have a common identifier must match, otherwise the condition is invalid. Implementations may return messages as a map of `MESSAGE_ID` => `MESSAGE` pairs.\r\n\r\nThe message to be signed is the concatenation of the `FIXED_PREFIX` and `DYNAMIC_MESSAGE`.\r\n\r\nThe `MESSAGE_ID`, `FIXED_PREFIX`, `DYNAMIC_MESSAGE_LENGTH` and `DYNAMIC_MESSAGE` fields have the same meaning as in the [**RSA-SHA-256 condition type**](https://interledger.org/five-bells-condition/spec.html).\r\n\r\n### Usage\r\n\r\n```python\r\nfrom bigchaindb.crypto.ed25519 import Ed25519SigningKey, Ed25519VerifyingKey\r\nfrom bigchaindb.crypto.fulfillments.ed25519_sha256 import Ed25519Sha256Fulfillment\r\n\r\n# We use base58 key encoding\r\nsk = Ed25519SigningKey(b'9qLvREC54mhKYivr88VpckyVWdAFmifJpGjbvV5AiTRs')\r\nvk = sk.get_verifying_key()\r\n\r\n# Create an ED25519-SHA256 condition\r\ned25519_fulfill`nt = Ed25519Sha256Fulfillment()\r\ned25519_fulfillment.public_key = vk\r\ned25519_fulfillment.message_prefix = 'Hello world!'\r\ned25519_fulfillment.max_dynamic_message_length = 32  # defaults to 0\r\ned25519_condition_uri = ed25519_fulfillment.condition.serialize_uri()\r\nprint (ed25519_condition_uri)\r\n# prints 'cc:1:8:qQINW2um59C4DB9JSVXH1igqAmaYGGqryllHUgCpfPU:113'\r\n\r\n# ED25519-SHA256 condition not fulfilled\r\nprint(ed25519_fulfillment.validate())\r\n# prints False\r\n\r\n# Fulfill an ED25519-SHA256 condition\r\ned25519_fulfillment.message = ' Conditions are here!'\r\ned25519_fulfillment.sign(sk)\r\nprint(ed25519_fulfillment.validate())\r\n# prints True\r\n\r\nprint(ed25519_fulfillment.serialize_uri())\r\n# prints \r\n#  'cf:1:8:IOwXK5OtXlY79JMscOEkUDTDVGfvLv1NZOv4GWg0Z-K_DEhlbGxvIHdvcmxkI\r\n#   SAVIENvbmRpdGlvbnMgYXJlIGhlcmUhQENbql531PbCJlRUvKjP56k0XKJMOrIGo2F66u\r\n#   euTtRnYrJB2t2ZttdfXM4gzD_87eH1nZTpu4rTkAx81hSdpwI'\r\nprint (ed25519_fulfillment.condition.serialize_uri())\r\n\r\n# Parse a fulfillment URI\r\nparsed_ed25519_fulfillment = Ed25519Sha256Fulfillment.from_uri('cf:1:8:IOwXK5OtXlY79JMscOEkUDTDVGfvLv1NZOv4GWg0Z-K_DEhlbGxvIHdvcmxkISAVIENvbmRpdGlvbnMgYXJlIGhlcmUhQENbql531PbCJlRUvKjP56k0XKJMOrIGo2F66ueuTtRnYrJB2t2ZttdfXM4gzD_87eH1nZTpu4rTkAx81hSdpwI')\r\n        \r\nprint(parsed_ed25519_fulfillment.validate())\r\n# prints True\r\nprint(parsed_ed25519_fulfillment.condition.serialize_uri())\r\n# prints 'cc:1:8:qQINW2um59C4DB9JSVXH1igqAmaYGGqryllHUgCpfPU:113'\r\n```\r\n\r\n### Implementation\r\n\r\nThe exact algorithm and encodings used for `PUBLIC_KEY` and `SIGNATURE` are Ed25519 as defined in [draft-irtf-cfrg-eddsa-04](https://datatracker.ietf.org/doc/draft-irtf-cfrg-eddsa/).\r\n\r\n## THRESHOLD-SHA-256\r\n\r\nTHRESHOLD-SHA-256 is assigned the type bit 2<sup>2</sup> = 0x04.\r\n\r\n### Condition\r\n\r\n```\r\nHASH = SHA256(\r\n  VARUINT TYPE_BIT\r\n  VARUINT THRESHOLD\r\n  VARARRAY\r\n    VARUINT WEIGHT\r\n    CONDITION\r\n)\r\n```\r\n\r\nThe `TYPE_BIT` is `0x04`. The reason we need this is because threshold conditions are a structural condition. Structural conditions can have subconditions, meaning their TYPE_BITMASK can have multiple bits set, including other structural conditions. This `TYPE_BIT` prevents the possibility that two different structural fulfillments could ever generate the exact same condition.\r\n\r\nThe `VARARRAY` of conditions is sorted first based on length, shortest first. Elements of the same length are sorted in lexicographic (big-endian) order, smallest first.\r\n\r\n### Fulfillment\r\n\r\n```\r\nFULFILLMENT_PAYLOAD =\r\n  VARUINT THRESHOLD\r\n  VARARRAY\r\n    VARUINT WEIGHT\r\n    FULFILLMENT\r\n  VARARRAY\r\n    VARUINT WEIGHT\r\n    CONDITION\r\n```\r\n\r\n### Usage\r\n\r\n```python\r\nfrom bigchaindb.crypto.fulfillments.sha256 import Sha256Fulfillment\r\nfrom bigchaindb.crypto.fulfillments.ed25519_sha256 import Ed25519Sha256Fulfillment\r\nfrom bigchaindb.crypto.fulfillments.threshold_sha256 import ThresholdSha256Fulfillment\r\n\r\n# Parse some fulfillments\r\nsha256_fulfillment = Sha256Fulfillment.from_uri('cf:1:1:AA')\r\ned25519_fulfillment = Ed25519Sha256Fulfillment.from_uri('cf:1:8:IOwXK5OtXlY79JMscOEkUDTDVGfvLv1NZOv4GWg0Z-K_DEhlbGxvIHdvcmxkISAVIENvbmRpdGlvbnMgYXJlIGhlcmUhQENbql531PbCJlRUvKjP56k0XKJMOrIGo2F66ueuTtRnYrJB2t2ZttdfXM4gzD_87eH1nZTpu4rTkAx81hSdpwI')\r\n\r\n# Create a threshold condition\r\nthreshold_fulfillment = ThresholdSha256Fulfillment()\r\nthreshold_fulfillment.add_subfulfillment(sha256_fulfillment)\r\nthreshold_fulfillment.add_subfulfillment(ed25519_fulfillment)\r\nthreshold_fulfillment.threshold = 1  # OR gate \r\nprint(threshold_fulfillment.condition.serialize_uri())\r\n# prints 'cc:1:d:9DdkQtOl2m9yjqZzCg6ck5b2zM3tAPLlJMaHsKkszIA:114'\r\n\r\n# Compile a threshold fulfillment\r\nthreshold_fulfillment_uri = threshold_fulfillment.serialize_uri()\r\n# Note: If there are more than enough fulfilled subconditions, shorter\r\n# fulfillments will be chosen over longer ones.\r\nprint(threshold_fulfillment_uri)\r\n# prints 'cf:1:4:AQEBAQABAQggqQINW2um59C4DB9JSVXH1igqAmaYGGqryllHUgCpfPVx'\r\n\r\n# Validate fulfillment\r\nprint(threshold_fulfillment.validate())\r\n# prints True\r\n\r\n# Parse the fulfillment\r\nreparsed_fulfillment = \\\r\n    ThresholdSha256Fulfillment.from_uri(threshold_fulfillment_uri)\r\nprint(reparsed_fulfillment.validate())\r\n# prints True\r\n\r\n# Increase threshold to a 3-port AND gate\r\nthreshold_fulfillment.threshold = 3\r\nprint(threshold_fulfillment.validate())\r\n# prints False\r\n\r\n# Create a nested threshold condition\r\n# VALID = SHA and DSA and (DSA or DSA) \r\nnested_fulfillment = ThresholdSha256Fulfillment()\r\nnested_fulfillment.add_subfulfillment(ed25519_fulfillment)\r\nnested_fulfillment.add_subfulfillment(ed25519_fulfillment)\r\nnested_fulfillment.threshold = 1  # OR gate \r\n\r\nthreshold_fulfillment.add_subfulfillment(nested_fulfillment)\r\nprint(threshold_fulfillment.serialize_uri())\r\n# prints \r\n#  'cf:1:4:AwMBAQABCCDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfivwxIZWxsby\r\n#   B3b3JsZCEgFSBDb25kaXRpb25zIGFyZSBoZXJlIUBDW6ped9T2wiZUVLyoz-epNFyiTDq\r\n#   yBqNheurnrk7UZ2KyQdrdmbbXX1zOIMw__O3h9Z2U6buK05AMfNYUnacCAQQBAQEIIOwX\r\n#   K5OtXlY79JMscOEkUDTDVGfvLv1NZOv4GWg0Z-K_DEhlbGxvIHdvcmxkISAVIENvbmRpd\r\n#   GlvbnMgYXJlIGhlcmUhQENbql531PbCJlRUvKjP56k0XKJMOrIGo2F66ueuTtRnYrJB2t\r\n#   2ZttdfXM4gzD_87eH1nZTpu4rTkAx81hSdpwIBAQggqQINW2um59C4DB9JSVXH1igqAma\r\n#   YGGqryllHUgCpfPVxAA'\r\n```","created_at":"2016-03-18T21:25:30Z","updated_at":"2016-03-21T10:31:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"de1daa3f8d12e8831a7e3fc3525846a32e17338c","assignee":{"login":"vrde","id":134680,"avatar_url":"https://avatars.githubusercontent.com/u/134680?v=3","gravatar_id":"","url":"https://api.github.com/users/vrde","html_url":"https://github.com/vrde","followers_url":"https://api.github.com/users/vrde/followers","following_url":"https://api.github.com/users/vrde/following{/other_user}","gists_url":"https://api.github.com/users/vrde/gists{/gist_id}","starred_url":"https://api.github.com/users/vrde/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vrde/subscriptions","organizations_url":"https://api.github.com/users/vrde/orgs","repos_url":"https://api.github.com/users/vrde/repos","events_url":"https://api.github.com/users/vrde/events{/privacy}","received_events_url":"https://api.github.com/users/vrde/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146/commits","review_comments_url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146/comments","review_comment_url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/comments{/number}","comments_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/146/comments","statuses_url":"https://api.github.com/repos/bigchaindb/bigchaindb/statuses/9c0e9f62315cc22f2b50d09b510665e1e4f3d165","head":{"label":"bigchaindb:feat/127/crypto-conditions-ilp-sha256-fulfillments","ref":"feat/127/crypto-conditions-ilp-sha256-fulfillments","sha":"9c0e9f62315cc22f2b50d09b510665e1e4f3d165","user":{"login":"bigchaindb","id":17097586,"avatar_url":"https://avatars.githubusercontent.com/u/17097586?v=3","gravatar_id":"","url":"https://api.github.com/users/bigchaindb","html_url":"https://github.com/bigchaindb","followers_url":"https://api.github.com/users/bigchaindb/followers","following_url":"https://api.github.com/users/bigchaindb/following{/other_user}","gists_url":"https://api.github.com/users/bigchaindb/gists{/gist_id}","starred_url":"https://api.github.com/users/bigchaindb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bigchaindb/subscriptions","organizations_url":"https://api.github.com/users/bigchaindb/orgs","repos_url":"https://api.github.com/users/bigchaindb/repos","events_url":"https://api.github.com/users/bigchaindb/events{/privacy}","received_events_url":"https://api.github.com/users/bigchaindb/received_events","type":"Organization","site_admin":false},"repo":{"id":51290852,"name":"bigchaindb","full_name":"bigchaindb/bigchaindb","owner":{"login":"bigchaindb","id":17097586,"avatar_url":"https://avatars.githubusercontent.com/u/17097586?v=3","gravatar_id":"","url":"https://api.github.com/users/bigchaindb","html_url":"https://github.com/bigchaindb","followers_url":"https://api.github.com/users/bigchaindb/followers","following_url":"https://api.github.com/users/bigchaindb/following{/other_user}","gists_url":"https://api.github.com/users/bigchaindb/gists{/gist_id}","starred_url":"https://api.github.com/users/bigchaindb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bigchaindb/subscriptions","organizations_url":"https://api.github.com/users/bigchaindb/orgs","repos_url":"https://api.github.com/users/bigchaindb/repos","events_url":"https://api.github.com/users/bigchaindb/events{/privacy}","received_events_url":"https://api.github.com/users/bigchaindb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/bigchaindb/bigchaindb","description":"A scalable blockchain database","fork":false,"url":"https://api.github.com/repos/bigchaindb/bigchaindb","forks_url":"https://api.github.com/repos/bigchaindb/bigchaindb/forks","keys_url":"https://api.github.com/repos/bigchaindb/bigchaindb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bigchaindb/bigchaindb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bigchaindb/bigchaindb/teams","hooks_url":"https://api.github.com/repos/bigchaindb/bigchaindb/hooks","issue_events_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/events{/number}","events_url":"https://api.github.com/repos/bigchaindb/bigchaindb/events","assignees_url":"https://api.github.com/repos/bigchaindb/bigchaindb/assignees{/user}","branches_url":"https://api.github.com/repos/bigchaindb/bigchaindb/branches{/branch}","tags_url":"https://api.github.com/repos/bigchaindb/bigchaindb/tags","blobs_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/refs{/sha}","trees_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bigchaindb/bigchaindb/statuses/{sha}","languages_url":"https://api.github.com/repos/bigchaindb/bigchaindb/languages","stargazers_url":"https://api.github.com/repos/bigchaindb/bigchaindb/stargazers","contributors_url":"https://api.github.com/repos/bigchaindb/bigchaindb/contributors","subscribers_url":"https://api.github.com/repos/bigchaindb/bigchaindb/subscribers","subscription_url":"https://api.github.com/repos/bigchaindb/bigchaindb/subscription","commits_url":"https://api.github.com/repos/bigchaindb/bigchaindb/commits{/sha}","git_commits_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/commits{/sha}","comments_url":"https://api.github.com/repos/bigchaindb/bigchaindb/comments{/number}","issue_comment_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/comments{/number}","contents_url":"https://api.github.com/repos/bigchaindb/bigchaindb/contents/{+path}","compare_url":"https://api.github.com/repos/bigchaindb/bigchaindb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bigchaindb/bigchaindb/merges","archive_url":"https://api.github.com/repos/bigchaindb/bigchaindb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bigchaindb/bigchaindb/downloads","issues_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues{/number}","pulls_url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls{/number}","milestones_url":"https://api.github.com/repos/bigchaindb/bigchaindb/milestones{/number}","notifications_url":"https://api.github.com/repos/bigchaindb/bigchaindb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bigchaindb/bigchaindb/labels{/name}","releases_url":"https://api.github.com/repos/bigchaindb/bigchaindb/releases{/id}","deployments_url":"https://api.github.com/repos/bigchaindb/bigchaindb/deployments","created_at":"2016-02-08T10:12:58Z","updated_at":"2016-03-21T09:08:51Z","pushed_at":"2016-03-21T10:12:44Z","git_url":"git://github.com/bigchaindb/bigchaindb.git","ssh_url":"git@github.com:bigchaindb/bigchaindb.git","clone_url":"https://github.com/bigchaindb/bigchaindb.git","svn_url":"https://github.com/bigchaindb/bigchaindb","homepage":"https://www.bigchaindb.com/","size":845,"stargazers_count":374,"watchers_count":374,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":38,"mirror_url":null,"open_issues_count":34,"forks":38,"open_issues":34,"watchers":374,"default_branch":"develop"}},"base":{"label":"bigchaindb:develop","ref":"develop","sha":"4dcd25e8aca0b7de5ee32320be1261ee6125d3bc","user":{"login":"bigchaindb","id":17097586,"avatar_url":"https://avatars.githubusercontent.com/u/17097586?v=3","gravatar_id":"","url":"https://api.github.com/users/bigchaindb","html_url":"https://github.com/bigchaindb","followers_url":"https://api.github.com/users/bigchaindb/followers","following_url":"https://api.github.com/users/bigchaindb/following{/other_user}","gists_url":"https://api.github.com/users/bigchaindb/gists{/gist_id}","starred_url":"https://api.github.com/users/bigchaindb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bigchaindb/subscriptions","organizations_url":"https://api.github.com/users/bigchaindb/orgs","repos_url":"https://api.github.com/users/bigchaindb/repos","events_url":"https://api.github.com/users/bigchaindb/events{/privacy}","received_events_url":"https://api.github.com/users/bigchaindb/received_events","type":"Organization","site_admin":false},"repo":{"id":51290852,"name":"bigchaindb","full_name":"bigchaindb/bigchaindb","owner":{"login":"bigchaindb","id":17097586,"avatar_url":"https://avatars.githubusercontent.com/u/17097586?v=3","gravatar_id":"","url":"https://api.github.com/users/bigchaindb","html_url":"https://github.com/bigchaindb","followers_url":"https://api.github.com/users/bigchaindb/followers","following_url":"https://api.github.com/users/bigchaindb/following{/other_user}","gists_url":"https://api.github.com/users/bigchaindb/gists{/gist_id}","starred_url":"https://api.github.com/users/bigchaindb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bigchaindb/subscriptions","organizations_url":"https://api.github.com/users/bigchaindb/orgs","repos_url":"https://api.github.com/users/bigchaindb/repos","events_url":"https://api.github.com/users/bigchaindb/events{/privacy}","received_events_url":"https://api.github.com/users/bigchaindb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/bigchaindb/bigchaindb","description":"A scalable blockchain database","fork":false,"url":"https://api.github.com/repos/bigchaindb/bigchaindb","forks_url":"https://api.github.com/repos/bigchaindb/bigchaindb/forks","keys_url":"https://api.github.com/repos/bigchaindb/bigchaindb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bigchaindb/bigchaindb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bigchaindb/bigchaindb/teams","hooks_url":"https://api.github.com/repos/bigchaindb/bigchaindb/hooks","issue_events_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/events{/number}","events_url":"https://api.github.com/repos/bigchaindb/bigchaindb/events","assignees_url":"https://api.github.com/repos/bigchaindb/bigchaindb/assignees{/user}","branches_url":"https://api.github.com/repos/bigchaindb/bigchaindb/branches{/branch}","tags_url":"https://api.github.com/repos/bigchaindb/bigchaindb/tags","blobs_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/refs{/sha}","trees_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bigchaindb/bigchaindb/statuses/{sha}","languages_url":"https://api.github.com/repos/bigchaindb/bigchaindb/languages","stargazers_url":"https://api.github.com/repos/bigchaindb/bigchaindb/stargazers","contributors_url":"https://api.github.com/repos/bigchaindb/bigchaindb/contributors","subscribers_url":"https://api.github.com/repos/bigchaindb/bigchaindb/subscribers","subscription_url":"https://api.github.com/repos/bigchaindb/bigchaindb/subscription","commits_url":"https://api.github.com/repos/bigchaindb/bigchaindb/commits{/sha}","git_commits_url":"https://api.github.com/repos/bigchaindb/bigchaindb/git/commits{/sha}","comments_url":"https://api.github.com/repos/bigchaindb/bigchaindb/comments{/number}","issue_comment_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/comments{/number}","contents_url":"https://api.github.com/repos/bigchaindb/bigchaindb/contents/{+path}","compare_url":"https://api.github.com/repos/bigchaindb/bigchaindb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bigchaindb/bigchaindb/merges","archive_url":"https://api.github.com/repos/bigchaindb/bigchaindb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bigchaindb/bigchaindb/downloads","issues_url":"https://api.github.com/repos/bigchaindb/bigchaindb/issues{/number}","pulls_url":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls{/number}","milestones_url":"https://api.github.com/repos/bigchaindb/bigchaindb/milestones{/number}","notifications_url":"https://api.github.com/repos/bigchaindb/bigchaindb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bigchaindb/bigchaindb/labels{/name}","releases_url":"https://api.github.com/repos/bigchaindb/bigchaindb/releases{/id}","deployments_url":"https://api.github.com/repos/bigchaindb/bigchaindb/deployments","created_at":"2016-02-08T10:12:58Z","updated_at":"2016-03-21T09:08:51Z","pushed_at":"2016-03-21T10:12:44Z","git_url":"git://github.com/bigchaindb/bigchaindb.git","ssh_url":"git@github.com:bigchaindb/bigchaindb.git","clone_url":"https://github.com/bigchaindb/bigchaindb.git","svn_url":"https://github.com/bigchaindb/bigchaindb","homepage":"https://www.bigchaindb.com/","size":845,"stargazers_count":374,"watchers_count":374,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":38,"mirror_url":null,"open_issues_count":34,"forks":38,"open_issues":34,"watchers":374,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146"},"html":{"href":"https://github.com/bigchaindb/bigchaindb/pull/146"},"issue":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/146"},"comments":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/issues/146/comments"},"review_comments":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146/comments"},"review_comment":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/pulls/146/commits"},"statuses":{"href":"https://api.github.com/repos/bigchaindb/bigchaindb/statuses/9c0e9f62315cc22f2b50d09b510665e1e4f3d165"}}}}