github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/common/rpc/convert/execution_data.go (about)

     1  package convert
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"google.golang.org/grpc/codes"
     7  	"google.golang.org/grpc/status"
     8  
     9  	"github.com/onflow/flow/protobuf/go/flow/entities"
    10  
    11  	"github.com/onflow/flow-go/ledger"
    12  	"github.com/onflow/flow-go/model/flow"
    13  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    14  )
    15  
    16  // BlockExecutionDataEventPayloadsToVersion converts all event payloads to version
    17  func BlockExecutionDataEventPayloadsToVersion(
    18  	m *entities.BlockExecutionData,
    19  	to entities.EventEncodingVersion,
    20  ) error {
    21  	if to == entities.EventEncodingVersion_CCF_V0 {
    22  		return nil
    23  	}
    24  
    25  	for i, chunk := range m.ChunkExecutionData {
    26  		for j, e := range chunk.Events {
    27  			converted, err := CcfPayloadToJsonPayload(e.Payload)
    28  			if err != nil {
    29  				return fmt.Errorf("failed to convert payload for event %d to json: %w", j, err)
    30  			}
    31  			m.ChunkExecutionData[i].Events[j].Payload = converted
    32  		}
    33  	}
    34  	return nil
    35  }
    36  
    37  // BlockExecutionDataToMessage converts a BlockExecutionData to a protobuf message
    38  func BlockExecutionDataToMessage(data *execution_data.BlockExecutionData) (
    39  	*entities.BlockExecutionData,
    40  	error,
    41  ) {
    42  	chunkExecutionDatas := make([]*entities.ChunkExecutionData, len(data.ChunkExecutionDatas))
    43  	for i, chunk := range data.ChunkExecutionDatas {
    44  		chunkMessage, err := ChunkExecutionDataToMessage(chunk)
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  		chunkExecutionDatas[i] = chunkMessage
    49  	}
    50  	return &entities.BlockExecutionData{
    51  		BlockId:            IdentifierToMessage(data.BlockID),
    52  		ChunkExecutionData: chunkExecutionDatas,
    53  	}, nil
    54  }
    55  
    56  // MessageToBlockExecutionData converts a protobuf message to a BlockExecutionData
    57  func MessageToBlockExecutionData(
    58  	m *entities.BlockExecutionData,
    59  	chain flow.Chain,
    60  ) (*execution_data.BlockExecutionData, error) {
    61  	if m == nil {
    62  		return nil, ErrEmptyMessage
    63  	}
    64  	chunks := make([]*execution_data.ChunkExecutionData, len(m.ChunkExecutionData))
    65  	for i, chunk := range m.GetChunkExecutionData() {
    66  		convertedChunk, err := MessageToChunkExecutionData(chunk, chain)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  		chunks[i] = convertedChunk
    71  	}
    72  
    73  	return &execution_data.BlockExecutionData{
    74  		BlockID:             MessageToIdentifier(m.GetBlockId()),
    75  		ChunkExecutionDatas: chunks,
    76  	}, nil
    77  }
    78  
    79  // ChunkExecutionDataToMessage converts a ChunkExecutionData to a protobuf message
    80  func ChunkExecutionDataToMessage(data *execution_data.ChunkExecutionData) (
    81  	*entities.ChunkExecutionData,
    82  	error,
    83  ) {
    84  	collection := &entities.ExecutionDataCollection{}
    85  	if data.Collection != nil {
    86  		collection = &entities.ExecutionDataCollection{
    87  			Transactions: TransactionsToMessages(data.Collection.Transactions),
    88  		}
    89  	}
    90  
    91  	events := EventsToMessages(data.Events)
    92  	if len(events) == 0 {
    93  		events = nil
    94  	}
    95  
    96  	trieUpdate, err := TrieUpdateToMessage(data.TrieUpdate)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	var results []*entities.ExecutionDataTransactionResult
   102  	if len(data.TransactionResults) > 0 {
   103  		results = make([]*entities.ExecutionDataTransactionResult, len(data.TransactionResults))
   104  		for i, result := range data.TransactionResults {
   105  			results[i] = &entities.ExecutionDataTransactionResult{
   106  				TransactionId:   IdentifierToMessage(result.TransactionID),
   107  				Failed:          result.Failed,
   108  				ComputationUsed: result.ComputationUsed,
   109  			}
   110  		}
   111  	}
   112  
   113  	return &entities.ChunkExecutionData{
   114  		Collection:         collection,
   115  		Events:             events,
   116  		TrieUpdate:         trieUpdate,
   117  		TransactionResults: results,
   118  	}, nil
   119  }
   120  
   121  // MessageToChunkExecutionData converts a protobuf message to a ChunkExecutionData
   122  func MessageToChunkExecutionData(
   123  	m *entities.ChunkExecutionData,
   124  	chain flow.Chain,
   125  ) (*execution_data.ChunkExecutionData, error) {
   126  	collection, err := messageToTrustedCollection(m.GetCollection(), chain)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	var trieUpdate *ledger.TrieUpdate
   132  	if m.GetTrieUpdate() != nil {
   133  		trieUpdate, err = MessageToTrieUpdate(m.GetTrieUpdate())
   134  		if err != nil {
   135  			return nil, err
   136  		}
   137  	}
   138  
   139  	events := MessagesToEvents(m.GetEvents())
   140  	if len(events) == 0 {
   141  		events = nil
   142  	}
   143  
   144  	var results []flow.LightTransactionResult
   145  	if len(m.GetTransactionResults()) > 0 {
   146  		results = make([]flow.LightTransactionResult, len(m.GetTransactionResults()))
   147  		for i, result := range m.GetTransactionResults() {
   148  			results[i] = flow.LightTransactionResult{
   149  				TransactionID:   MessageToIdentifier(result.GetTransactionId()),
   150  				Failed:          result.GetFailed(),
   151  				ComputationUsed: result.GetComputationUsed(),
   152  			}
   153  		}
   154  	}
   155  
   156  	return &execution_data.ChunkExecutionData{
   157  		Collection:         collection,
   158  		Events:             events,
   159  		TrieUpdate:         trieUpdate,
   160  		TransactionResults: results,
   161  	}, nil
   162  }
   163  
   164  // MessageToTrieUpdate converts a protobuf message to a TrieUpdate
   165  func MessageToTrieUpdate(m *entities.TrieUpdate) (*ledger.TrieUpdate, error) {
   166  	rootHash, err := ledger.ToRootHash(m.GetRootHash())
   167  	if err != nil {
   168  		return nil, fmt.Errorf("could not convert root hash: %w", err)
   169  	}
   170  
   171  	paths := make([]ledger.Path, len(m.GetPaths()))
   172  	for i, path := range m.GetPaths() {
   173  		convertedPath, err := ledger.ToPath(path)
   174  		if err != nil {
   175  			return nil, fmt.Errorf("could not convert path %d: %w", i, err)
   176  		}
   177  		paths[i] = convertedPath
   178  	}
   179  
   180  	payloads := make([]*ledger.Payload, len(m.Payloads))
   181  	for i, payload := range m.GetPayloads() {
   182  		keyParts := make([]ledger.KeyPart, len(payload.GetKeyPart()))
   183  		for j, keypart := range payload.GetKeyPart() {
   184  			keyParts[j] = ledger.NewKeyPart(uint16(keypart.GetType()), keypart.GetValue())
   185  		}
   186  		payloads[i] = ledger.NewPayload(ledger.NewKey(keyParts), payload.GetValue())
   187  	}
   188  
   189  	return &ledger.TrieUpdate{
   190  		RootHash: rootHash,
   191  		Paths:    paths,
   192  		Payloads: payloads,
   193  	}, nil
   194  }
   195  
   196  // TrieUpdateToMessage converts a TrieUpdate to a protobuf message
   197  func TrieUpdateToMessage(t *ledger.TrieUpdate) (*entities.TrieUpdate, error) {
   198  	if t == nil {
   199  		return nil, nil
   200  	}
   201  
   202  	paths := make([][]byte, len(t.Paths))
   203  	for i := range t.Paths {
   204  		paths[i] = t.Paths[i][:]
   205  	}
   206  
   207  	payloads := make([]*entities.Payload, len(t.Payloads))
   208  	for i, payload := range t.Payloads {
   209  		key, err := payload.Key()
   210  		if err != nil {
   211  			return nil, fmt.Errorf("could not convert payload %d: %w", i, err)
   212  		}
   213  		keyParts := make([]*entities.KeyPart, len(key.KeyParts))
   214  		for j, keyPart := range key.KeyParts {
   215  			keyParts[j] = &entities.KeyPart{
   216  				Type:  uint32(keyPart.Type),
   217  				Value: keyPart.Value,
   218  			}
   219  		}
   220  		payloads[i] = &entities.Payload{
   221  			KeyPart: keyParts,
   222  			Value:   payload.Value(),
   223  		}
   224  	}
   225  
   226  	return &entities.TrieUpdate{
   227  		RootHash: t.RootHash[:],
   228  		Paths:    paths,
   229  		Payloads: payloads,
   230  	}, nil
   231  }
   232  
   233  // messageToTrustedCollection converts a protobuf message to a collection using the
   234  // messageToTrustedTransaction converter to support service transactions.
   235  func messageToTrustedCollection(
   236  	m *entities.ExecutionDataCollection,
   237  	chain flow.Chain,
   238  ) (*flow.Collection, error) {
   239  	messages := m.GetTransactions()
   240  	if len(messages) == 0 {
   241  		return &flow.Collection{}, nil
   242  	}
   243  
   244  	transactions := make([]*flow.TransactionBody, len(messages))
   245  	for i, message := range messages {
   246  		transaction, err := messageToTrustedTransaction(message, chain)
   247  		if err != nil {
   248  			return nil, fmt.Errorf("could not convert transaction %d: %w", i, err)
   249  		}
   250  		transactions[i] = &transaction
   251  	}
   252  
   253  	return &flow.Collection{Transactions: transactions}, nil
   254  }
   255  
   256  // messageToTrustedTransaction converts a transaction message to a transaction body.
   257  // This is useful when converting transactions from trusted state like BlockExecutionData which
   258  // contain service transactions that do not conform to external transaction format.
   259  func messageToTrustedTransaction(
   260  	m *entities.Transaction,
   261  	chain flow.Chain,
   262  ) (flow.TransactionBody, error) {
   263  	if m == nil {
   264  		return flow.TransactionBody{}, ErrEmptyMessage
   265  	}
   266  
   267  	t := flow.NewTransactionBody()
   268  
   269  	proposalKey := m.GetProposalKey()
   270  	if proposalKey != nil {
   271  		proposalAddress, err := insecureAddress(proposalKey.GetAddress())
   272  		if err != nil {
   273  			return *t, fmt.Errorf("could not convert proposer address: %w", err)
   274  		}
   275  		t.SetProposalKey(proposalAddress, uint64(proposalKey.GetKeyId()), proposalKey.GetSequenceNumber())
   276  	}
   277  
   278  	payer := m.GetPayer()
   279  	if payer != nil {
   280  		payerAddress, err := insecureAddress(payer)
   281  		if err != nil {
   282  			return *t, fmt.Errorf("could not convert payer address: %w", err)
   283  		}
   284  		t.SetPayer(payerAddress)
   285  	}
   286  
   287  	for _, authorizer := range m.GetAuthorizers() {
   288  		authorizerAddress, err := Address(authorizer, chain)
   289  		if err != nil {
   290  			return *t, fmt.Errorf("could not convert authorizer address: %w", err)
   291  		}
   292  		t.AddAuthorizer(authorizerAddress)
   293  	}
   294  
   295  	for _, sig := range m.GetPayloadSignatures() {
   296  		addr, err := Address(sig.GetAddress(), chain)
   297  		if err != nil {
   298  			return *t, fmt.Errorf("could not convert payload signature address: %w", err)
   299  		}
   300  		t.AddPayloadSignature(addr, uint64(sig.GetKeyId()), sig.GetSignature())
   301  	}
   302  
   303  	for _, sig := range m.GetEnvelopeSignatures() {
   304  		addr, err := Address(sig.GetAddress(), chain)
   305  		if err != nil {
   306  			return *t, fmt.Errorf("could not convert envelope signature address: %w", err)
   307  		}
   308  		t.AddEnvelopeSignature(addr, uint64(sig.GetKeyId()), sig.GetSignature())
   309  	}
   310  
   311  	t.SetScript(m.GetScript())
   312  	t.SetArguments(m.GetArguments())
   313  	t.SetReferenceBlockID(flow.HashToID(m.GetReferenceBlockId()))
   314  	t.SetComputeLimit(m.GetGasLimit())
   315  
   316  	return *t, nil
   317  }
   318  
   319  func MessageToRegisterID(m *entities.RegisterID, chain flow.Chain) (flow.RegisterID, error) {
   320  	if m == nil {
   321  		return flow.RegisterID{}, ErrEmptyMessage
   322  	}
   323  
   324  	owner := flow.EmptyAddress
   325  	if len(m.GetOwner()) > 0 {
   326  		var err error
   327  		owner, err = Address(m.GetOwner(), chain)
   328  		if err != nil {
   329  			return flow.RegisterID{}, fmt.Errorf("could not convert owner address: %w", err)
   330  		}
   331  	}
   332  
   333  	key := string(m.GetKey())
   334  
   335  	return flow.NewRegisterID(owner, key), nil
   336  }
   337  
   338  // MessagesToRegisterIDs converts a protobuf message to RegisterIDs
   339  func MessagesToRegisterIDs(m []*entities.RegisterID, chain flow.Chain) (flow.RegisterIDs, error) {
   340  	if m == nil {
   341  		return nil, ErrEmptyMessage
   342  	}
   343  	result := make(flow.RegisterIDs, len(m))
   344  	for i, entry := range m {
   345  		regID, err := MessageToRegisterID(entry, chain)
   346  		if err != nil {
   347  			return nil, fmt.Errorf("failed to convert register id %d: %w", i, err)
   348  		}
   349  		result[i] = regID
   350  	}
   351  	return result, nil
   352  }
   353  
   354  func RegisterIDToMessage(id flow.RegisterID) *entities.RegisterID {
   355  	return &entities.RegisterID{
   356  		Owner: []byte(id.Owner),
   357  		Key:   []byte(id.Key),
   358  	}
   359  }
   360  
   361  // insecureAddress converts a raw address to a flow.Address, skipping validation
   362  // This is useful when converting transactions from trusted state like BlockExecutionData.
   363  // This should only be used for trusted inputs
   364  func insecureAddress(rawAddress []byte) (flow.Address, error) {
   365  	if len(rawAddress) == 0 {
   366  		return flow.EmptyAddress, status.Error(codes.InvalidArgument, "address cannot be empty")
   367  	}
   368  
   369  	return flow.BytesToAddress(rawAddress), nil
   370  }