github.com/koko1123/flow-go-1@v0.29.6/engine/execution/state/state.go (about) 1 package state 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "github.com/davecgh/go-spew/spew" 9 "github.com/dgraph-io/badger/v3" 10 11 "github.com/koko1123/flow-go-1/engine/execution/state/delta" 12 "github.com/koko1123/flow-go-1/ledger" 13 "github.com/koko1123/flow-go-1/model/flow" 14 "github.com/koko1123/flow-go-1/model/messages" 15 "github.com/koko1123/flow-go-1/module" 16 "github.com/koko1123/flow-go-1/module/mempool/entity" 17 "github.com/koko1123/flow-go-1/module/trace" 18 "github.com/koko1123/flow-go-1/storage" 19 badgerstorage "github.com/koko1123/flow-go-1/storage/badger" 20 "github.com/koko1123/flow-go-1/storage/badger/operation" 21 "github.com/koko1123/flow-go-1/storage/badger/procedure" 22 ) 23 24 // ReadOnlyExecutionState allows to read the execution state 25 type ReadOnlyExecutionState interface { 26 // NewView creates a new ready-only view at the given state commitment. 27 NewView(flow.StateCommitment) *delta.View 28 29 GetRegisters( 30 context.Context, 31 flow.StateCommitment, 32 []flow.RegisterID, 33 ) ([]flow.RegisterValue, error) 34 35 GetProof( 36 context.Context, 37 flow.StateCommitment, 38 []flow.RegisterID, 39 ) (flow.StorageProof, error) 40 41 // StateCommitmentByBlockID returns the final state commitment for the provided block ID. 42 StateCommitmentByBlockID(context.Context, flow.Identifier) (flow.StateCommitment, error) 43 44 // HasState returns true if the state with the given state commitment exists in memory 45 HasState(flow.StateCommitment) bool 46 47 // ChunkDataPackByChunkID retrieve a chunk data pack given the chunk ID. 48 ChunkDataPackByChunkID(flow.Identifier) (*flow.ChunkDataPack, error) 49 50 GetExecutionResultID(context.Context, flow.Identifier) (flow.Identifier, error) 51 52 RetrieveStateDelta(context.Context, flow.Identifier) (*messages.ExecutionStateDelta, error) 53 54 GetHighestExecutedBlockID(context.Context) (uint64, flow.Identifier, error) 55 56 GetCollection(identifier flow.Identifier) (*flow.Collection, error) 57 58 GetBlockIDByChunkID(chunkID flow.Identifier) (flow.Identifier, error) 59 } 60 61 // TODO Many operations here are should be transactional, so we need to refactor this 62 // to store a reference to DB and compose operations and procedures rather then 63 // just being amalgamate of proxies for single transactions operation 64 65 // ExecutionState is an interface used to access and mutate the execution state of the blockchain. 66 type ExecutionState interface { 67 ReadOnlyExecutionState 68 69 UpdateHighestExecutedBlockIfHigher(context.Context, *flow.Header) error 70 71 SaveExecutionResults(ctx context.Context, header *flow.Header, endState flow.StateCommitment, 72 chunkDataPacks []*flow.ChunkDataPack, 73 executionReceipt *flow.ExecutionReceipt, events []flow.EventsList, serviceEvents flow.EventsList, results []flow.TransactionResult) error 74 } 75 76 const ( 77 KeyPartOwner = uint16(0) 78 // @deprecated - controller was used only by the very first 79 // version of cadence for access controll which was retired later on 80 // KeyPartController = uint16(1) 81 KeyPartKey = uint16(2) 82 ) 83 84 type state struct { 85 tracer module.Tracer 86 ls ledger.Ledger 87 commits storage.Commits 88 blocks storage.Blocks 89 headers storage.Headers 90 collections storage.Collections 91 chunkDataPacks storage.ChunkDataPacks 92 results storage.ExecutionResults 93 myReceipts storage.MyExecutionReceipts 94 events storage.Events 95 serviceEvents storage.ServiceEvents 96 transactionResults storage.TransactionResults 97 db *badger.DB 98 } 99 100 func RegisterIDToKey(reg flow.RegisterID) ledger.Key { 101 return ledger.NewKey([]ledger.KeyPart{ 102 ledger.NewKeyPart(KeyPartOwner, []byte(reg.Owner)), 103 ledger.NewKeyPart(KeyPartKey, []byte(reg.Key)), 104 }) 105 } 106 107 // NewExecutionState returns a new execution state access layer for the given ledger storage. 108 func NewExecutionState( 109 ls ledger.Ledger, 110 commits storage.Commits, 111 blocks storage.Blocks, 112 headers storage.Headers, 113 collections storage.Collections, 114 chunkDataPacks storage.ChunkDataPacks, 115 results storage.ExecutionResults, 116 myReceipts storage.MyExecutionReceipts, 117 events storage.Events, 118 serviceEvents storage.ServiceEvents, 119 transactionResults storage.TransactionResults, 120 db *badger.DB, 121 tracer module.Tracer, 122 ) ExecutionState { 123 return &state{ 124 tracer: tracer, 125 ls: ls, 126 commits: commits, 127 blocks: blocks, 128 headers: headers, 129 collections: collections, 130 chunkDataPacks: chunkDataPacks, 131 results: results, 132 myReceipts: myReceipts, 133 events: events, 134 serviceEvents: serviceEvents, 135 transactionResults: transactionResults, 136 db: db, 137 } 138 139 } 140 141 func makeSingleValueQuery(commitment flow.StateCommitment, owner, key string) (*ledger.QuerySingleValue, error) { 142 return ledger.NewQuerySingleValue(ledger.State(commitment), 143 RegisterIDToKey(flow.NewRegisterID(owner, key)), 144 ) 145 } 146 147 func makeQuery(commitment flow.StateCommitment, ids []flow.RegisterID) (*ledger.Query, error) { 148 149 keys := make([]ledger.Key, len(ids)) 150 for i, id := range ids { 151 keys[i] = RegisterIDToKey(id) 152 } 153 154 return ledger.NewQuery(ledger.State(commitment), keys) 155 } 156 157 func RegisterIDSToKeys(ids []flow.RegisterID) []ledger.Key { 158 keys := make([]ledger.Key, len(ids)) 159 for i, id := range ids { 160 keys[i] = RegisterIDToKey(id) 161 } 162 return keys 163 } 164 165 func RegisterValuesToValues(values []flow.RegisterValue) []ledger.Value { 166 vals := make([]ledger.Value, len(values)) 167 for i, value := range values { 168 vals[i] = value 169 } 170 return vals 171 } 172 173 func LedgerGetRegister(ldg ledger.Ledger, commitment flow.StateCommitment) delta.GetRegisterFunc { 174 175 readCache := make(map[flow.RegisterID]flow.RegisterEntry) 176 177 return func(owner, key string) (flow.RegisterValue, error) { 178 regID := flow.RegisterID{ 179 Owner: owner, 180 Key: key, 181 } 182 183 if value, ok := readCache[regID]; ok { 184 return value.Value, nil 185 } 186 187 query, err := makeSingleValueQuery(commitment, owner, key) 188 189 if err != nil { 190 return nil, fmt.Errorf("cannot create ledger query: %w", err) 191 } 192 193 value, err := ldg.GetSingleValue(query) 194 195 if err != nil { 196 return nil, fmt.Errorf("error getting register (%s) value at %x: %w", key, commitment, err) 197 } 198 199 // Prevent caching of value with len zero 200 if len(value) == 0 { 201 return nil, nil 202 } 203 204 // don't cache value with len zero 205 readCache[regID] = flow.RegisterEntry{Key: regID, Value: value} 206 207 return value, nil 208 } 209 } 210 211 func (s *state) NewView(commitment flow.StateCommitment) *delta.View { 212 return delta.NewView(LedgerGetRegister(s.ls, commitment)) 213 } 214 215 type RegisterUpdatesHolder interface { 216 RegisterUpdates() ([]flow.RegisterID, []flow.RegisterValue) 217 } 218 219 func CommitDelta(ldg ledger.Ledger, ruh RegisterUpdatesHolder, baseState flow.StateCommitment) (flow.StateCommitment, *ledger.TrieUpdate, error) { 220 ids, values := ruh.RegisterUpdates() 221 222 update, err := ledger.NewUpdate( 223 ledger.State(baseState), 224 RegisterIDSToKeys(ids), 225 RegisterValuesToValues(values), 226 ) 227 228 if err != nil { 229 return flow.DummyStateCommitment, nil, fmt.Errorf("cannot create ledger update: %w", err) 230 } 231 232 commit, trieUpdate, err := ldg.Set(update) 233 if err != nil { 234 return flow.DummyStateCommitment, nil, err 235 } 236 237 return flow.StateCommitment(commit), trieUpdate, nil 238 } 239 240 //func (s *state) CommitDelta(ctx context.Context, delta delta.Delta, baseState flow.StateCommitment) (flow.StateCommitment, error) { 241 // span, _ := s.tracer.StartSpanFromContext(ctx, trace.EXECommitDelta) 242 // defer span.End() 243 // 244 // return CommitDelta(s.ls, delta, baseState) 245 //} 246 247 func (s *state) getRegisters(commit flow.StateCommitment, registerIDs []flow.RegisterID) (*ledger.Query, []ledger.Value, error) { 248 249 query, err := makeQuery(commit, registerIDs) 250 251 if err != nil { 252 return nil, nil, fmt.Errorf("cannot create ledger query: %w", err) 253 } 254 255 values, err := s.ls.Get(query) 256 if err != nil { 257 return nil, nil, fmt.Errorf("cannot query ledger: %w", err) 258 } 259 260 return query, values, err 261 } 262 263 func (s *state) GetRegisters( 264 ctx context.Context, 265 commit flow.StateCommitment, 266 registerIDs []flow.RegisterID, 267 ) ([]flow.RegisterValue, error) { 268 span, _ := s.tracer.StartSpanFromContext(ctx, trace.EXEGetRegisters) 269 defer span.End() 270 271 _, values, err := s.getRegisters(commit, registerIDs) 272 if err != nil { 273 return nil, err 274 } 275 276 registerValues := make([]flow.RegisterValue, len(values)) 277 for i, v := range values { 278 registerValues[i] = v 279 } 280 281 return registerValues, nil 282 } 283 284 func (s *state) GetProof( 285 ctx context.Context, 286 commit flow.StateCommitment, 287 registerIDs []flow.RegisterID, 288 ) (flow.StorageProof, error) { 289 290 span, _ := s.tracer.StartSpanFromContext(ctx, trace.EXEGetRegistersWithProofs) 291 defer span.End() 292 293 query, err := makeQuery(commit, registerIDs) 294 295 if err != nil { 296 return nil, fmt.Errorf("cannot create ledger query: %w", err) 297 } 298 299 // Get proofs in an arbitrary order, not correlated to the register ID order in the query. 300 proof, err := s.ls.Prove(query) 301 if err != nil { 302 return nil, fmt.Errorf("cannot get proof: %w", err) 303 } 304 return proof, nil 305 } 306 307 func (s *state) HasState(commitment flow.StateCommitment) bool { 308 return s.ls.HasState(ledger.State(commitment)) 309 } 310 311 func (s *state) StateCommitmentByBlockID(ctx context.Context, blockID flow.Identifier) (flow.StateCommitment, error) { 312 return s.commits.ByBlockID(blockID) 313 } 314 315 func (s *state) ChunkDataPackByChunkID(chunkID flow.Identifier) (*flow.ChunkDataPack, error) { 316 chunkDataPack, err := s.chunkDataPacks.ByChunkID(chunkID) 317 if err != nil { 318 return nil, fmt.Errorf("could not retrieve stored chunk data pack: %w", err) 319 } 320 321 return chunkDataPack, nil 322 } 323 324 func (s *state) GetExecutionResultID(ctx context.Context, blockID flow.Identifier) (flow.Identifier, error) { 325 span, _ := s.tracer.StartSpanFromContext(ctx, trace.EXEGetExecutionResultID) 326 defer span.End() 327 328 result, err := s.results.ByBlockID(blockID) 329 if err != nil { 330 return flow.ZeroID, err 331 } 332 return result.ID(), nil 333 } 334 335 func (s *state) SaveExecutionResults(ctx context.Context, header *flow.Header, endState flow.StateCommitment, 336 chunkDataPacks []*flow.ChunkDataPack, executionReceipt *flow.ExecutionReceipt, events []flow.EventsList, serviceEvents flow.EventsList, 337 results []flow.TransactionResult) error { 338 return s.saveExecutionResults(ctx, header, endState, chunkDataPacks, executionReceipt, events, serviceEvents, results) 339 } 340 341 func (s *state) saveExecutionResults(ctx context.Context, header *flow.Header, endState flow.StateCommitment, 342 chunkDataPacks []*flow.ChunkDataPack, executionReceipt *flow.ExecutionReceipt, events []flow.EventsList, serviceEvents flow.EventsList, 343 results []flow.TransactionResult) error { 344 345 spew.Config.DisableMethods = true 346 spew.Config.DisablePointerMethods = true 347 348 span, childCtx := s.tracer.StartSpanFromContext(ctx, trace.EXEStateSaveExecutionResults) 349 defer span.End() 350 351 blockID := header.ID() 352 353 // Write Batch is BadgerDB feature designed for handling lots of writes 354 // in efficient and automatic manner, hence pushing all the updates we can 355 // as tightly as possible to let Badger manage it. 356 // Note, that it does not guarantee atomicity as transactions has size limit, 357 // but it's the closest thing to atomicity we could have 358 batch := badgerstorage.NewBatch(s.db) 359 360 for _, chunkDataPack := range chunkDataPacks { 361 err := s.chunkDataPacks.BatchStore(chunkDataPack, batch) 362 if err != nil { 363 return fmt.Errorf("cannot store chunk data pack: %w", err) 364 } 365 366 err = s.headers.BatchIndexByChunkID(header.ID(), chunkDataPack.ChunkID, batch) 367 if err != nil { 368 return fmt.Errorf("cannot index chunk data pack by blockID: %w", err) 369 } 370 } 371 372 err := s.commits.BatchStore(blockID, endState, batch) 373 if err != nil { 374 return fmt.Errorf("cannot store state commitment: %w", err) 375 } 376 377 err = s.events.BatchStore(blockID, events, batch) 378 if err != nil { 379 return fmt.Errorf("cannot store events: %w", err) 380 } 381 382 err = s.serviceEvents.BatchStore(blockID, serviceEvents, batch) 383 if err != nil { 384 return fmt.Errorf("cannot store service events: %w", err) 385 } 386 387 err = s.transactionResults.BatchStore(blockID, results, batch) 388 if err != nil { 389 return fmt.Errorf("cannot store transaction result: %w", err) 390 } 391 392 executionResult := &executionReceipt.ExecutionResult 393 err = s.results.BatchStore(executionResult, batch) 394 if err != nil { 395 return fmt.Errorf("cannot store execution result: %w", err) 396 } 397 398 err = s.results.BatchIndex(blockID, executionResult.ID(), batch) 399 if err != nil { 400 return fmt.Errorf("cannot index execution result: %w", err) 401 } 402 403 err = s.myReceipts.BatchStoreMyReceipt(executionReceipt, batch) 404 if err != nil { 405 return fmt.Errorf("could not persist execution result: %w", err) 406 } 407 408 err = batch.Flush() 409 if err != nil { 410 return fmt.Errorf("batch flush error: %w", err) 411 } 412 413 //outside batch because it requires read access 414 err = s.UpdateHighestExecutedBlockIfHigher(childCtx, header) 415 if err != nil { 416 return fmt.Errorf("cannot update highest executed block: %w", err) 417 } 418 return nil 419 } 420 421 func (s *state) RetrieveStateDelta(ctx context.Context, blockID flow.Identifier) (*messages.ExecutionStateDelta, error) { 422 // TODO: consider using storage.Index.ByBlockID, the index contains collection id and seals ID 423 block, err := s.blocks.ByID(blockID) 424 if err != nil { 425 return nil, fmt.Errorf("cannot retrieve block: %w", err) 426 } 427 completeCollections := make(map[flow.Identifier]*entity.CompleteCollection) 428 429 for _, guarantee := range block.Payload.Guarantees { 430 collection, err := s.collections.ByID(guarantee.CollectionID) 431 if err != nil { 432 return nil, fmt.Errorf("cannot retrieve collection for delta: %w", err) 433 } 434 completeCollections[collection.ID()] = &entity.CompleteCollection{ 435 Guarantee: guarantee, 436 Transactions: collection.Transactions, 437 } 438 } 439 440 var startStateCommitment flow.StateCommitment 441 var endStateCommitment flow.StateCommitment 442 var stateInteractions []*delta.Snapshot 443 var events []flow.Event 444 var serviceEvents []flow.Event 445 var txResults []flow.TransactionResult 446 447 err = s.db.View(func(txn *badger.Txn) error { 448 err = operation.LookupStateCommitment(blockID, &endStateCommitment)(txn) 449 if err != nil { 450 return fmt.Errorf("cannot lookup state commitment: %w", err) 451 452 } 453 454 err = operation.LookupStateCommitment(block.Header.ParentID, &startStateCommitment)(txn) 455 if err != nil { 456 return fmt.Errorf("cannot lookup parent state commitment: %w", err) 457 } 458 459 err = operation.LookupEventsByBlockID(blockID, &events)(txn) 460 if err != nil { 461 return fmt.Errorf("cannot lookup events: %w", err) 462 } 463 464 err = operation.LookupServiceEventsByBlockID(blockID, &serviceEvents)(txn) 465 if err != nil { 466 return fmt.Errorf("cannot lookup events: %w", err) 467 } 468 469 err = operation.LookupTransactionResultsByBlockID(blockID, &txResults)(txn) 470 if err != nil { 471 return fmt.Errorf("cannot lookup transaction errors: %w", err) 472 } 473 474 err = operation.RetrieveExecutionStateInteractions(blockID, &stateInteractions)(txn) 475 if err != nil { 476 return fmt.Errorf("cannot lookup execution state views: %w", err) 477 } 478 479 return nil 480 }) 481 if err != nil { 482 return nil, err 483 } 484 485 return &messages.ExecutionStateDelta{ 486 ExecutableBlock: entity.ExecutableBlock{ 487 Block: block, 488 StartState: &startStateCommitment, 489 CompleteCollections: completeCollections, 490 }, 491 StateInteractions: stateInteractions, 492 EndState: endStateCommitment, 493 Events: events, 494 ServiceEvents: serviceEvents, 495 TransactionResults: txResults, 496 }, nil 497 } 498 499 func (s *state) GetCollection(identifier flow.Identifier) (*flow.Collection, error) { 500 return s.collections.ByID(identifier) 501 } 502 503 func (s *state) GetBlockIDByChunkID(chunkID flow.Identifier) (flow.Identifier, error) { 504 return s.headers.IDByChunkID(chunkID) 505 } 506 507 func (s *state) UpdateHighestExecutedBlockIfHigher(ctx context.Context, header *flow.Header) error { 508 if s.tracer != nil { 509 span, _ := s.tracer.StartSpanFromContext(ctx, trace.EXEUpdateHighestExecutedBlockIfHigher) 510 defer span.End() 511 } 512 513 return operation.RetryOnConflict(s.db.Update, procedure.UpdateHighestExecutedBlockIfHigher(header)) 514 } 515 516 func (s *state) GetHighestExecutedBlockID(ctx context.Context) (uint64, flow.Identifier, error) { 517 var blockID flow.Identifier 518 var height uint64 519 err := s.db.View(procedure.GetHighestExecutedBlock(&height, &blockID)) 520 if err != nil { 521 return 0, flow.ZeroID, err 522 } 523 524 return height, blockID, nil 525 } 526 527 // IsBlockExecuted returns whether the block has been executed. 528 // it checks whether the state commitment exists in execution state. 529 func IsBlockExecuted(ctx context.Context, state ReadOnlyExecutionState, block flow.Identifier) (bool, error) { 530 _, err := state.StateCommitmentByBlockID(ctx, block) 531 532 // statecommitment exists means the block has been executed 533 if err == nil { 534 return true, nil 535 } 536 537 // statecommitment not exists means the block hasn't been executed yet 538 if errors.Is(err, storage.ErrNotFound) { 539 return false, nil 540 } 541 542 return false, err 543 }