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 }