github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/chunk.go (about)

     1  package flow
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/ipfs/go-cid"
     7  )
     8  
     9  var EmptyEventCollectionID Identifier
    10  
    11  func init() {
    12  	// Convert hexadecimal string to a byte slice.
    13  	var err error
    14  	emptyEventCollectionHex := "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"
    15  	EmptyEventCollectionID, err = HexStringToIdentifier(emptyEventCollectionHex)
    16  	if err != nil {
    17  		log.Fatalf("Failed to decode hex: %v", err)
    18  	}
    19  }
    20  
    21  type ChunkBody struct {
    22  	CollectionIndex uint
    23  
    24  	// execution info
    25  	StartState      StateCommitment // start state when starting executing this chunk
    26  	EventCollection Identifier      // Events generated by executing results
    27  	BlockID         Identifier      // Block id of the execution result this chunk belongs to
    28  
    29  	// Computation consumption info
    30  	TotalComputationUsed uint64 // total amount of computation used by running all txs in this chunk
    31  	NumberOfTransactions uint64 // number of transactions inside the collection
    32  }
    33  
    34  type Chunk struct {
    35  	ChunkBody
    36  
    37  	Index uint64 // chunk index inside the ER (starts from zero)
    38  	// EndState inferred from next chunk or from the ER
    39  	EndState StateCommitment
    40  }
    41  
    42  func NewChunk(
    43  	blockID Identifier,
    44  	collectionIndex int,
    45  	startState StateCommitment,
    46  	numberOfTransactions int,
    47  	eventCollection Identifier,
    48  	endState StateCommitment,
    49  	totalComputationUsed uint64,
    50  ) *Chunk {
    51  	return &Chunk{
    52  		ChunkBody: ChunkBody{
    53  			BlockID:              blockID,
    54  			CollectionIndex:      uint(collectionIndex),
    55  			StartState:           startState,
    56  			NumberOfTransactions: uint64(numberOfTransactions),
    57  			EventCollection:      eventCollection,
    58  			TotalComputationUsed: totalComputationUsed,
    59  		},
    60  		Index:    uint64(collectionIndex),
    61  		EndState: endState,
    62  	}
    63  }
    64  
    65  // ID returns a unique id for this entity
    66  func (ch *Chunk) ID() Identifier {
    67  	return MakeID(ch.ChunkBody)
    68  }
    69  
    70  // Checksum provides a cryptographic commitment for a chunk content
    71  func (ch *Chunk) Checksum() Identifier {
    72  	return MakeID(ch)
    73  }
    74  
    75  // ChunkDataPack holds all register touches (any read, or write).
    76  //
    77  // Note that we have to include merkle paths as storage proof for all registers touched (read or written) for
    78  // the _starting_ state of the chunk (i.e. before the chunk computation updates the registers).
    79  // For instance, if an execution state contains three registers: { A: 1, B: 2, C: 3}, and a certain
    80  // chunk has a tx that assigns A = A + B, then its chunk data pack should include the merkle
    81  // paths for { A: 1, B: 2 } as storage proof.
    82  // C is not included because it's neither read or written by the chunk.
    83  // B is included because it's read by the chunk.
    84  // A is included because it's updated by the chunk, and its value 1 is included because it's
    85  // the value before the chunk computation.
    86  // This is necessary for Verification Nodes to (i) check that the read register values are
    87  // consistent with the starting state's root hash and (ii) verify the correctness of the resulting
    88  // state after the chunk computation. `Proof` includes merkle proofs for all touched registers
    89  // during the execution of the chunk.
    90  // Register proofs order must not be correlated to the order of register reads during
    91  // the chunk execution in order to enforce the SPoCK secret high entropy.
    92  type ChunkDataPack struct {
    93  	ChunkID    Identifier      // ID of the chunk this data pack is for
    94  	StartState StateCommitment // commitment for starting state
    95  	Proof      StorageProof    // proof for all registers touched (read or written) during the chunk execution
    96  	Collection *Collection     // collection executed in this chunk
    97  
    98  	// ExecutionDataRoot is the root data structure of an execution_data.BlockExecutionData.
    99  	// It contains the necessary information for a verification node to validate that the
   100  	// BlockExecutionData produced is valid.
   101  	ExecutionDataRoot BlockExecutionDataRoot
   102  }
   103  
   104  // NewChunkDataPack returns an initialized chunk data pack.
   105  func NewChunkDataPack(
   106  	chunkID Identifier,
   107  	startState StateCommitment,
   108  	proof StorageProof,
   109  	collection *Collection,
   110  	execDataRoot BlockExecutionDataRoot,
   111  ) *ChunkDataPack {
   112  	return &ChunkDataPack{
   113  		ChunkID:           chunkID,
   114  		StartState:        startState,
   115  		Proof:             proof,
   116  		Collection:        collection,
   117  		ExecutionDataRoot: execDataRoot,
   118  	}
   119  }
   120  
   121  // ID returns the unique identifier for the concrete view, which is the ID of
   122  // the chunk the view is for.
   123  func (c *ChunkDataPack) ID() Identifier {
   124  	return c.ChunkID
   125  }
   126  
   127  // Checksum returns the checksum of the chunk data pack.
   128  func (c *ChunkDataPack) Checksum() Identifier {
   129  	return MakeID(c)
   130  }
   131  
   132  // TODO: This is the basic version of the list, we need to substitute it with something like Merkle tree at some point
   133  type ChunkList []*Chunk
   134  
   135  func (cl ChunkList) Fingerprint() Identifier {
   136  	return MerkleRoot(GetIDs(cl)...)
   137  }
   138  
   139  func (cl *ChunkList) Insert(ch *Chunk) {
   140  	*cl = append(*cl, ch)
   141  }
   142  
   143  func (cl ChunkList) Items() []*Chunk {
   144  	return cl
   145  }
   146  
   147  // Empty returns true if the chunk list is empty. Otherwise it returns false.
   148  func (cl ChunkList) Empty() bool {
   149  	return len(cl) == 0
   150  }
   151  
   152  func (cl ChunkList) Indices() []uint64 {
   153  	indices := make([]uint64, len(cl))
   154  	for i, chunk := range cl {
   155  		indices[i] = chunk.Index
   156  	}
   157  
   158  	return indices
   159  }
   160  
   161  // ByChecksum returns an entity from the list by entity fingerprint
   162  func (cl ChunkList) ByChecksum(cs Identifier) (*Chunk, bool) {
   163  	for _, ch := range cl {
   164  		if ch.Checksum() == cs {
   165  			return ch, true
   166  		}
   167  	}
   168  	return nil, false
   169  }
   170  
   171  // ByIndex returns an entity from the list by index
   172  // if requested chunk is within range of list, it returns chunk and true
   173  // if requested chunk is out of the range, it returns nil and false
   174  // boolean return value indicates whether requested chunk is within range
   175  func (cl ChunkList) ByIndex(i uint64) (*Chunk, bool) {
   176  	if i >= uint64(len(cl)) {
   177  		// index out of range
   178  		return nil, false
   179  	}
   180  	return cl[i], true
   181  }
   182  
   183  // Len returns the number of Chunks in the list. It is also part of the sort
   184  // interface that makes ChunkList sortable
   185  func (cl ChunkList) Len() int {
   186  	return len(cl)
   187  }
   188  
   189  // BlockExecutionDataRoot represents the root of a serialized execution_data.BlockExecutionData.
   190  // The hash of the serialized BlockExecutionDataRoot is the ExecutionDataID used within an
   191  // flow.ExecutionResult.
   192  // Context:
   193  //   - The trie updates in BlockExecutionDataRoot contain the _mutated_ registers only, which is
   194  //     helpful for clients to truslessly replicate the state.
   195  //   - In comparison, the chunk data packs contains all the register values at the chunk's starting
   196  //     state that were _touched_ (written and/or read). This is necessary for Verification Nodes to
   197  //     re-run the chunk the computation.
   198  type BlockExecutionDataRoot struct {
   199  	// BlockID is the ID of the block, whose result this execution data is for.
   200  	BlockID Identifier
   201  
   202  	// ChunkExecutionDataIDs is a list of the root CIDs for each serialized execution_data.ChunkExecutionData
   203  	// associated with this block.
   204  	ChunkExecutionDataIDs []cid.Cid
   205  }