github.com/braveheart12/just@v0.8.7/network/cascade/cascade.go (about)

     1  /*
     2   * The Clear BSD License
     3   *
     4   * Copyright (c) 2019 Insolar Technologies
     5   *
     6   * All rights reserved.
     7   *
     8   * Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
     9   *
    10   *  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    11   *  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    12   *  Neither the name of Insolar Technologies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    13   *
    14   * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    15   *
    16   */
    17  
    18  package cascade
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"math"
    24  	"sort"
    25  
    26  	"github.com/insolar/insolar/core"
    27  )
    28  
    29  func min(a, b int) int {
    30  	if a >= b {
    31  		return b
    32  	}
    33  	return a
    34  }
    35  
    36  // Cascade is struct to hold callback that sends cascade messages to next layers of cascade
    37  type Cascade struct {
    38  	SendMessage func(data core.Cascade, method string, args [][]byte) error
    39  }
    40  
    41  // SendToNextLayer sends data to callback.
    42  func (casc *Cascade) SendToNextLayer(data core.Cascade, method string, args [][]byte) error {
    43  	return casc.SendMessage(data, method, args)
    44  }
    45  
    46  // a - scale factor
    47  // r - common ratio
    48  // n - length of progression
    49  func geometricProgressionSum(a int, r int, n int) int {
    50  	S := int(math.Pow(float64(r), float64(n)))
    51  	return a * (1 - S) / (1 - r)
    52  }
    53  
    54  func calcHash(scheme core.PlatformCryptographyScheme, nodeID core.RecordRef, entropy core.Entropy) []byte {
    55  	data := make([]byte, core.RecordRefSize)
    56  	copy(data, nodeID[:])
    57  	for i, d := range data {
    58  		data[i] = entropy[i%core.EntropySize] ^ d
    59  	}
    60  
    61  	h := scheme.IntegrityHasher()
    62  	_, err := h.Write(data)
    63  	if err != nil {
    64  		panic(err)
    65  	}
    66  	return h.Sum(nil)
    67  }
    68  
    69  func getNextCascadeLayerIndexes(nodeIds []core.RecordRef, currentNode core.RecordRef, replicationFactor uint) (startIndex, endIndex int) {
    70  	depth := 0
    71  	j := 0
    72  	layerWidth := replicationFactor
    73  	found := false
    74  	// iterate to find current node in the nodes slice, incrementing j and depth according to replicationFactor
    75  	for _, nodeID := range nodeIds {
    76  		if nodeID == currentNode {
    77  			found = true
    78  			break
    79  		}
    80  		j++
    81  		if j == int(layerWidth) {
    82  			layerWidth *= replicationFactor
    83  			depth++
    84  			j = 0
    85  		}
    86  	}
    87  
    88  	if !found {
    89  		return len(nodeIds), len(nodeIds)
    90  	}
    91  
    92  	// calculate count of the all nodes that have depth less or equal to the current node
    93  	n := int(replicationFactor)
    94  	var layerWeight int
    95  	if n == 1 {
    96  		layerWeight = depth + 1
    97  	} else {
    98  		layerWeight = geometricProgressionSum(n, n, depth+1)
    99  	}
   100  	// calculate children subtree of the current node
   101  	startIndex = layerWeight + j*n
   102  	endIndex = startIndex + n
   103  	return
   104  }
   105  
   106  // CalculateNextNodes get nodes of the next cascade layer from the input nodes slice
   107  func CalculateNextNodes(scheme core.PlatformCryptographyScheme, data core.Cascade, currentNode *core.RecordRef) (nextNodeIds []core.RecordRef, err error) {
   108  	nodeIds := make([]core.RecordRef, len(data.NodeIds))
   109  	copy(nodeIds, data.NodeIds)
   110  
   111  	// catching possible panic from calcHash
   112  	defer func() {
   113  		if r := recover(); r != nil {
   114  			nextNodeIds, err = nil, fmt.Errorf("panic: %s", r)
   115  		}
   116  	}()
   117  
   118  	sort.SliceStable(nodeIds, func(i, j int) bool {
   119  		return bytes.Compare(
   120  			calcHash(scheme, nodeIds[i], data.Entropy),
   121  			calcHash(scheme, nodeIds[j], data.Entropy)) < 0
   122  	})
   123  
   124  	if currentNode == nil {
   125  		length := min(int(data.ReplicationFactor), len(nodeIds))
   126  		return nodeIds[:length], nil
   127  	}
   128  
   129  	// get indexes of the next layer nodes from the sorted nodes slice
   130  	startIndex, endIndex := getNextCascadeLayerIndexes(nodeIds, *currentNode, data.ReplicationFactor)
   131  
   132  	if startIndex >= len(nodeIds) {
   133  		return nil, nil
   134  	}
   135  	return nodeIds[startIndex:min(endIndex, len(nodeIds))], nil
   136  }