Source code for lb.signature

# Copyright 2017 The Lambda-blocks developers. See AUTHORS for details.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Signature is an algorithm which permits to uniquely identify a
particular block (instance) in a particular graph.

For that purpose, we hash the block name, the block parameters, and
the signatures of the block inputs (recursively).  This is similar to
a Merkle tree, in that a change in one of the block predecessors will
raise a different block signature.

Let H the signature algorithm, and h a cryptographically secure hash
function.

B is a block, whose inputs are:
  * right: ``bar.result``
  * left: ``foo.result``
and args are:
  * abc: ``123``
  * cba: ``['x', 'y']``

::

   H(block) = h([
     block.blockname,                 <--- the *block* name, not the *instance* name
     [
       ('abc', 123),                  <--+ ordered by arg name
       ('cba', ['x', 'y'])            <--|
     ],
     [
       ('left', H(foo), 'result'),    <--+ ordered by input name
       ('right', H(bar), 'result')    <--|
     ]
   ])

"""

import hashlib

signatures = {} # we keep the signatures cached
                # keys are block instance names, unique in a graph
                # might break if many DAGs are executed in the same python process

[docs]def sign(block): """ Returns a unique signature for a block instance within a graph, and keeps it cached to speed-up the recursive aspect. """ instance_name = block.fields['name'] # is it cached? if instance_name in signatures.keys(): return signatures[instance_name] name = block.fields['block'] args = [] for arg in sorted(block.fields['args'].keys()): args.append((arg, block.fields['args'][arg])) inputs = [] for input_ in sorted(block.prev_vertices, key=lambda x: x.value_dest): # input_ is a Connector inputs.append((input_.value_dest, sign(input_.block_from), input_.value_from)) r = [name, args, inputs] s = hashlib.sha256(repr(r).encode('utf8')).hexdigest() # we cache it signatures[instance_name] = s return s