github.com/braveheart12/insolar-09-08-19@v0.8.7/ledger/jetcoordinator/jetcoordinator.go (about)

     1  /*
     2   *    Copyright 2019 Insolar Technologies
     3   *
     4   *    Licensed under the Apache License, Version 2.0 (the "License");
     5   *    you may not use this file except in compliance with the License.
     6   *    You may obtain a copy of the License at
     7   *
     8   *        http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   *    Unless required by applicable law or agreed to in writing, software
    11   *    distributed under the License is distributed on an "AS IS" BASIS,
    12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *    See the License for the specific language governing permissions and
    14   *    limitations under the License.
    15   */
    16  
    17  package jetcoordinator
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"sort"
    24  
    25  	"github.com/insolar/insolar"
    26  	"github.com/insolar/insolar/core"
    27  	"github.com/insolar/insolar/ledger/storage"
    28  	"github.com/insolar/insolar/ledger/storage/jet"
    29  	"github.com/insolar/insolar/ledger/storage/nodes"
    30  	"github.com/insolar/insolar/utils/entropy"
    31  	"github.com/pkg/errors"
    32  )
    33  
    34  // JetCoordinator is responsible for all jet interactions
    35  type JetCoordinator struct {
    36  	NodeNet                    core.NodeNetwork                `inject:""`
    37  	PlatformCryptographyScheme core.PlatformCryptographyScheme `inject:""`
    38  	PulseStorage               core.PulseStorage               `inject:""`
    39  	JetStorage                 storage.JetStorage              `inject:""`
    40  	PulseTracker               storage.PulseTracker            `inject:""`
    41  	Nodes                      nodes.Accessor                  `inject:""`
    42  
    43  	lightChainLimit int
    44  }
    45  
    46  // NewJetCoordinator creates new coordinator instance.
    47  func NewJetCoordinator(lightChainLimit int) *JetCoordinator {
    48  	return &JetCoordinator{lightChainLimit: lightChainLimit}
    49  }
    50  
    51  // Hardcoded roles count for validation and execution
    52  const (
    53  	VirtualValidatorCount  = 3
    54  	MaterialValidatorCount = 3
    55  
    56  	VirtualExecutorCount  = 1
    57  	MaterialExecutorCount = 1
    58  )
    59  
    60  // Me returns current node.
    61  func (jc *JetCoordinator) Me() core.RecordRef {
    62  	return jc.NodeNet.GetOrigin().ID()
    63  }
    64  
    65  // IsAuthorized checks for role on concrete pulse for the address.
    66  func (jc *JetCoordinator) IsAuthorized(
    67  	ctx context.Context,
    68  	role core.DynamicRole,
    69  	obj core.RecordID,
    70  	pulse core.PulseNumber,
    71  	node core.RecordRef,
    72  ) (bool, error) {
    73  	nodes, err := jc.QueryRole(ctx, role, obj, pulse)
    74  	if err != nil {
    75  		return false, err
    76  	}
    77  	for _, n := range nodes {
    78  		if n == node {
    79  			return true, nil
    80  		}
    81  	}
    82  	return false, nil
    83  }
    84  
    85  // QueryRole returns node refs responsible for role bound operations for given object and pulse.
    86  func (jc *JetCoordinator) QueryRole(
    87  	ctx context.Context,
    88  	role core.DynamicRole,
    89  	objID core.RecordID,
    90  	pulse core.PulseNumber,
    91  ) ([]core.RecordRef, error) {
    92  	switch role {
    93  	case core.DynamicRoleVirtualExecutor:
    94  		node, err := jc.VirtualExecutorForObject(ctx, objID, pulse)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  		return []core.RecordRef{*node}, nil
    99  
   100  	case core.DynamicRoleVirtualValidator:
   101  		return jc.VirtualValidatorsForObject(ctx, objID, pulse)
   102  
   103  	case core.DynamicRoleLightExecutor:
   104  		if objID.Pulse() == core.PulseNumberJet {
   105  			node, err := jc.LightExecutorForJet(ctx, objID, pulse)
   106  			if err != nil {
   107  				return nil, err
   108  			}
   109  			return []core.RecordRef{*node}, nil
   110  		}
   111  		node, err := jc.LightExecutorForObject(ctx, objID, pulse)
   112  		if err != nil {
   113  			return nil, err
   114  		}
   115  		return []core.RecordRef{*node}, nil
   116  
   117  	case core.DynamicRoleLightValidator:
   118  		return jc.LightValidatorsForObject(ctx, objID, pulse)
   119  
   120  	case core.DynamicRoleHeavyExecutor:
   121  		node, err := jc.Heavy(ctx, pulse)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  		return []core.RecordRef{*node}, nil
   126  	}
   127  
   128  	panic("unexpected role")
   129  }
   130  
   131  // VirtualExecutorForObject returns list of VEs for a provided pulse and objID
   132  func (jc *JetCoordinator) VirtualExecutorForObject(
   133  	ctx context.Context, objID core.RecordID, pulse core.PulseNumber,
   134  ) (*core.RecordRef, error) {
   135  	nodes, err := jc.virtualsForObject(ctx, objID, pulse, VirtualExecutorCount)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	return &nodes[0], nil
   140  }
   141  
   142  // VirtualValidatorsForObject returns list of VVs for a provided pulse and objID
   143  func (jc *JetCoordinator) VirtualValidatorsForObject(
   144  	ctx context.Context, objID core.RecordID, pulse core.PulseNumber,
   145  ) ([]core.RecordRef, error) {
   146  	nodes, err := jc.virtualsForObject(ctx, objID, pulse, VirtualValidatorCount+VirtualExecutorCount)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	// Skipping `VirtualExecutorCount` for validators
   151  	// because it will be selected as the executor(s) for the same pulse.
   152  	return nodes[VirtualExecutorCount:], nil
   153  }
   154  
   155  // LightExecutorForJet returns list of LEs for a provided pulse and jetID
   156  func (jc *JetCoordinator) LightExecutorForJet(
   157  	ctx context.Context, jetID core.RecordID, pulse core.PulseNumber,
   158  ) (*core.RecordRef, error) {
   159  	nodes, err := jc.lightMaterialsForJet(ctx, jetID, pulse, MaterialExecutorCount)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	return &nodes[0], nil
   164  }
   165  
   166  // LightValidatorsForJet returns list of LVs for a provided pulse and jetID
   167  func (jc *JetCoordinator) LightValidatorsForJet(
   168  	ctx context.Context, jetID core.RecordID, pulse core.PulseNumber,
   169  ) ([]core.RecordRef, error) {
   170  	nodes, err := jc.lightMaterialsForJet(ctx, jetID, pulse, MaterialValidatorCount+MaterialExecutorCount)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	// Skipping `MaterialExecutorCount` for validators
   175  	// because it will be selected as the executor(s) for the same pulse.
   176  	return nodes[MaterialExecutorCount:], nil
   177  }
   178  
   179  // LightExecutorForObject returns list of LEs for a provided pulse and objID
   180  func (jc *JetCoordinator) LightExecutorForObject(
   181  	ctx context.Context, objID core.RecordID, pulse core.PulseNumber,
   182  ) (*core.RecordRef, error) {
   183  	jetID, _ := jc.JetStorage.FindJet(ctx, pulse, objID)
   184  	return jc.LightExecutorForJet(ctx, *jetID, pulse)
   185  }
   186  
   187  // LightValidatorsForObject returns list of LVs for a provided pulse and objID
   188  func (jc *JetCoordinator) LightValidatorsForObject(
   189  	ctx context.Context, objID core.RecordID, pulse core.PulseNumber,
   190  ) ([]core.RecordRef, error) {
   191  	jetID, _ := jc.JetStorage.FindJet(ctx, pulse, objID)
   192  	return jc.LightValidatorsForJet(ctx, *jetID, pulse)
   193  }
   194  
   195  // Heavy returns *core.RecorRef to a heavy of specific pulse
   196  func (jc *JetCoordinator) Heavy(ctx context.Context, pulse core.PulseNumber) (*core.RecordRef, error) {
   197  	candidates, err := jc.Nodes.InRole(pulse, core.StaticRoleHeavyMaterial)
   198  	if err == core.ErrNoNodes {
   199  		return nil, err
   200  	}
   201  	if err != nil {
   202  		return nil, errors.Wrapf(err, "failed to fetch active heavy nodes for pulse %v", pulse)
   203  	}
   204  	if len(candidates) == 0 {
   205  		return nil, errors.New(fmt.Sprintf("no active heavy nodes for pulse %d", pulse))
   206  	}
   207  	ent, err := jc.entropy(ctx, pulse)
   208  	if err != nil {
   209  		return nil, errors.Wrapf(err, "failed to fetch entropy for pulse %v", pulse)
   210  	}
   211  
   212  	refs, err := getRefs(
   213  		jc.PlatformCryptographyScheme,
   214  		ent[:],
   215  		candidates,
   216  		1,
   217  	)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	return &refs[0], nil
   222  }
   223  
   224  // IsBeyondLimit calculates if target pulse is behind clean-up limit
   225  // or if currentPN|targetPN didn't found in in-memory pulse-storage.
   226  func (jc *JetCoordinator) IsBeyondLimit(ctx context.Context, currentPN, targetPN core.PulseNumber) (bool, error) {
   227  	currentPulse, err := jc.PulseTracker.GetPulse(ctx, currentPN)
   228  	if err == core.ErrNotFound {
   229  		return true, nil
   230  	}
   231  	if err != nil {
   232  		return false, errors.Wrapf(err, "failed to fetch pulse %v", currentPN)
   233  	}
   234  
   235  	targetPulse, err := jc.PulseTracker.GetPulse(ctx, targetPN)
   236  	if err == core.ErrNotFound {
   237  		return true, nil
   238  	}
   239  	if err != nil {
   240  		return false, errors.Wrapf(err, "failed to fetch pulse %v", targetPN)
   241  	}
   242  
   243  	if currentPulse.SerialNumber-targetPulse.SerialNumber < jc.lightChainLimit {
   244  		return false, nil
   245  	}
   246  
   247  	return true, nil
   248  }
   249  
   250  // NodeForJet calculates a node (LME or heavy) for a specific jet for a specific pulseNumber
   251  func (jc *JetCoordinator) NodeForJet(ctx context.Context, jetID core.RecordID, rootPN, targetPN core.PulseNumber) (*core.RecordRef, error) {
   252  	toHeavy, err := jc.IsBeyondLimit(ctx, rootPN, targetPN)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	if toHeavy {
   258  		return jc.Heavy(ctx, rootPN)
   259  	}
   260  	return jc.LightExecutorForJet(ctx, jetID, targetPN)
   261  }
   262  
   263  // NodeForObject calculates a node (LME or heavy) for a specific jet for a specific pulseNumber
   264  func (jc *JetCoordinator) NodeForObject(ctx context.Context, objectID core.RecordID, rootPN, targetPN core.PulseNumber) (*core.RecordRef, error) {
   265  	toHeavy, err := jc.IsBeyondLimit(ctx, rootPN, targetPN)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	if toHeavy {
   271  		return jc.Heavy(ctx, rootPN)
   272  	}
   273  	return jc.LightExecutorForObject(ctx, objectID, targetPN)
   274  }
   275  
   276  func (jc *JetCoordinator) virtualsForObject(
   277  	ctx context.Context, objID core.RecordID, pulse core.PulseNumber, count int,
   278  ) ([]core.RecordRef, error) {
   279  	candidates, err := jc.Nodes.InRole(pulse, core.StaticRoleVirtual)
   280  	if err == core.ErrNoNodes {
   281  		return nil, err
   282  	}
   283  	if err != nil {
   284  		return nil, errors.Wrapf(err, "failed to fetch active virtual nodes for pulse %v", pulse)
   285  	}
   286  	if len(candidates) == 0 {
   287  		return nil, errors.New(fmt.Sprintf("no active virtual nodes for pulse %d", pulse))
   288  	}
   289  
   290  	ent, err := jc.entropy(ctx, pulse)
   291  	if err != nil {
   292  		return nil, errors.Wrapf(err, "failed to fetch entropy for pulse %v", pulse)
   293  	}
   294  
   295  	return getRefs(
   296  		jc.PlatformCryptographyScheme,
   297  		circleXOR(ent[:], objID.Hash()),
   298  		candidates,
   299  		count,
   300  	)
   301  }
   302  
   303  func (jc *JetCoordinator) lightMaterialsForJet(
   304  	ctx context.Context, jetID core.RecordID, pulse core.PulseNumber, count int,
   305  ) ([]core.RecordRef, error) {
   306  	_, prefix := jet.Jet(jetID)
   307  
   308  	candidates, err := jc.Nodes.InRole(pulse, core.StaticRoleLightMaterial)
   309  	if err == core.ErrNoNodes {
   310  		return nil, err
   311  	}
   312  	if err != nil {
   313  		return nil, errors.Wrapf(err, "failed to fetch active light nodes for pulse %v", pulse)
   314  	}
   315  	if len(candidates) == 0 {
   316  		return nil, core.ErrNoNodes
   317  	}
   318  
   319  	ent, err := jc.entropy(ctx, pulse)
   320  	if err != nil {
   321  		return nil, errors.Wrapf(err, "failed to fetch entropy for pulse %v", pulse)
   322  	}
   323  
   324  	return getRefs(
   325  		jc.PlatformCryptographyScheme,
   326  		circleXOR(ent[:], prefix),
   327  		candidates,
   328  		count,
   329  	)
   330  }
   331  
   332  func (jc *JetCoordinator) entropy(ctx context.Context, pulse core.PulseNumber) (core.Entropy, error) {
   333  	current, err := jc.PulseStorage.Current(ctx)
   334  	if err != nil {
   335  		return core.Entropy{}, errors.Wrap(err, "failed to get current pulse")
   336  	}
   337  
   338  	if current.PulseNumber == pulse {
   339  		return current.Entropy, nil
   340  	}
   341  
   342  	older, err := jc.PulseTracker.GetPulse(ctx, pulse)
   343  	if err != nil {
   344  		return core.Entropy{}, errors.Wrapf(err, "failed to fetch pulse data for pulse %v", pulse)
   345  	}
   346  
   347  	return older.Pulse.Entropy, nil
   348  }
   349  
   350  func getRefs(
   351  	scheme core.PlatformCryptographyScheme,
   352  	e []byte,
   353  	values []insolar.Node,
   354  	count int,
   355  ) ([]core.RecordRef, error) {
   356  	// TODO: remove sort when network provides sorted result from GetActiveNodesByRole (INS-890) - @nordicdyno 5.Dec.2018
   357  	sort.SliceStable(values, func(i, j int) bool {
   358  		v1 := values[i].ID
   359  		v2 := values[j].ID
   360  		return bytes.Compare(v1[:], v2[:]) < 0
   361  	})
   362  	in := make([]interface{}, 0, len(values))
   363  	for _, value := range values {
   364  		in = append(in, interface{}(value.ID))
   365  	}
   366  
   367  	res, err := entropy.SelectByEntropy(scheme, e, in, count)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  	out := make([]core.RecordRef, 0, len(res))
   372  	for _, value := range res {
   373  		out = append(out, value.(core.RecordRef))
   374  	}
   375  	return out, nil
   376  }
   377  
   378  // CircleXOR performs XOR for 'value' and 'src'. The result is returned as new byte slice.
   379  // If 'value' is smaller than 'dst', XOR starts from the beginning of 'src'.
   380  func circleXOR(value, src []byte) []byte {
   381  	result := make([]byte, len(value))
   382  	srcLen := len(src)
   383  	for i := range result {
   384  		result[i] = value[i] ^ src[i%srcLen]
   385  	}
   386  	return result
   387  }