// Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "log" "github.com/hyperledger/fabric-contract-api-go/contractapi" ) // Protobuf definitions type Uuid struct { Uuid string `json:"uuid"` } type DltRecordId struct { DomainUuid Uuid `json:"domain_uuid"` Type string `json:"type"` RecordUuid Uuid `json:"record_uuid"` } type DltRecord struct { RecordId DltRecordId `json:"record_id"` DataJson string `json:"data_json"` } type SmartContract struct { contractapi.Contract } // InitLedger activates the chaincode func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { return nil } func (s *SmartContract) StoreRecord(ctx contractapi.TransactionContextInterface, recordId DltRecordId, dataJson string) error { key, err := createHashKey(recordId) if err != nil { return fmt.Errorf("failed to create hash key: %v", err) } // Check if the same record does not exist before adding it to the ledger exists, err := s.RecordExists(ctx, key) if err == nil && exists != nil { return fmt.Errorf("the record %s already exists", key) } // Trigger an event if the transaction is successful storedRecord := DltRecord{ RecordId: recordId, DataJson: dataJson, } eventJson, err := json.Marshal(storedRecord) if err != nil { return fmt.Errorf("failed to marshal stored record: %v", err) } ctx.GetStub().SetEvent("StoreRecord", eventJson) // Store the record in the ledger return ctx.GetStub().PutState(key, []byte(dataJson)) } func (s *SmartContract) RetrieveRecord(ctx contractapi.TransactionContextInterface, recordId DltRecordId) (string, error) { key, err := createHashKey(recordId) if err != nil { return "", fmt.Errorf("failed to create hash key: %v", err) } // Get the record from the ledger dataBytes, err := ctx.GetStub().GetState(key) if err != nil || dataBytes == nil { return "", fmt.Errorf("data not found for key %s", key) } return string(dataBytes), nil } func (s *SmartContract) UpdateRecord(ctx contractapi.TransactionContextInterface, recordId DltRecordId, dataJson string) error { key, err := createHashKey(recordId) if err != nil { return fmt.Errorf("failed to create hash key: %v", err) } // Check if the record exists before updating it _, err = s.RecordExists(ctx, key) if err != nil { return err } // Trigger an event if the transaction is successful eventData := DltRecord{RecordId: recordId, DataJson: dataJson} eventJson, err := json.Marshal(eventData) if err != nil { return fmt.Errorf("failed to marshal event data: %v", err) } ctx.GetStub().SetEvent("UpdateRecord", eventJson) // Update the record in the ledger return ctx.GetStub().PutState(key, []byte(dataJson)) } func (s *SmartContract) DeleteRecord(ctx contractapi.TransactionContextInterface, recordId DltRecordId) error { key, err := createHashKey(recordId) if err != nil { return fmt.Errorf("failed to create hash key: %v", err) } // Check if the record exists before deleting it exists, err := s.RecordExists(ctx, key) if err != nil { return err } // Trigger an event if the transaction is successful eventData := DltRecord{RecordId: recordId, DataJson: string(exists)} eventJson, err := json.Marshal(eventData) if err != nil { return fmt.Errorf("failed to marshal event data: %v", err) } ctx.GetStub().SetEvent("DeleteRecord", eventJson) // Delete the record from the ledger return ctx.GetStub().DelState(key) } func (s *SmartContract) RecordExists(ctx contractapi.TransactionContextInterface, key string) ([]byte, error) { jsonData, err := ctx.GetStub().GetState(key) if err != nil { return nil, fmt.Errorf("failed to read from world state: %v", err) } if jsonData == nil { return nil, fmt.Errorf("the record %s does not exist", key) } return jsonData, nil } func (s *SmartContract) GetAllRecords(ctx contractapi.TransactionContextInterface) ([]map[string]interface{}, error) { // Range query with empty string for startKey and endKey does an // open-ended query of all records in the chaincode namespace. resultsIterator, err := ctx.GetStub().GetStateByRange("", "") if err != nil { return nil, err } defer resultsIterator.Close() var records []map[string]interface{} for resultsIterator.HasNext() { queryResponse, err := resultsIterator.Next() if err != nil { return nil, err } var generic map[string]interface{} if err := json.Unmarshal(queryResponse.Value, &generic); err != nil { return nil, fmt.Errorf("invalid JSON data: %v", err) } records = append(records, generic) } return records, nil } func createHashKey(recordId DltRecordId) (string, error) { recordIdJson, err := json.Marshal(recordId) if err != nil { return "", fmt.Errorf("failed to marshal record ID: %v", err) } hash := sha256.New() hash.Write(recordIdJson) return hex.EncodeToString(hash.Sum(nil)), nil } func main() { recordChaincode, err := contractapi.NewChaincode(&SmartContract{}) if err != nil { log.Panicf("Error creating chaincode: %v", err) } if err := recordChaincode.Start(); err != nil { log.Panicf("Error starting chaincode: %v", err) } }