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  }