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 }