From 934d88ccc06ea61cdc22bf8cdce7b5f3cad194d5 Mon Sep 17 00:00:00 2001 From: "Gomez Goiri, Aitor" <aitor.gomez@tecnalia.com> Date: Mon, 28 Feb 2022 16:49:04 +0100 Subject: [PATCH] Stats: getting and calculating total slag registered in the platform --- chaincode/controller/bid/bid_controller.go | 2 - chaincode/controller/operations.go | 20 +++- .../controller/stats/stats_controller.go | 51 ++++++++++ chaincode/controller/stats/stub.go | 94 +++++++++++++++++++ chaincode/main.go | 2 +- chaincode/middleware/acl.go | 11 +++ chaincode/middleware/stats.go | 92 ++++++++++++++++++ chaincode/model/stats.go | 17 ++++ 8 files changed, 282 insertions(+), 7 deletions(-) create mode 100644 chaincode/controller/stats/stats_controller.go create mode 100644 chaincode/controller/stats/stub.go create mode 100644 chaincode/middleware/stats.go create mode 100644 chaincode/model/stats.go diff --git a/chaincode/controller/bid/bid_controller.go b/chaincode/controller/bid/bid_controller.go index 225f65f..91d183e 100644 --- a/chaincode/controller/bid/bid_controller.go +++ b/chaincode/controller/bid/bid_controller.go @@ -11,7 +11,6 @@ import ( "git.code.tecnalia.com/ledgerbuilder/sdk/core/api" "git.code.tecnalia.com/ledgerbuilder/sdk/core/controller" "git.code.tecnalia.com/ledgerbuilder/sdk/core/fabric/protos" - "git.code.tecnalia.com/ledgerbuilder/sdk/core/util/logging" "git.code.tecnalia.com/ledgerbuilder/sdk/shared" "github.com/mitchellh/mapstructure" @@ -53,7 +52,6 @@ type Bid struct { } var ( - log = logging.NewGoLogger("controller/transfer") errInvalidInputData = errors.New("failed to read model") errParamsMissing = errors.New("missing split params. assetId and splitParams config must be send in split request") errChildrenMissmatch = errors.New("number of requested childs and proposed id count mismatch") diff --git a/chaincode/controller/operations.go b/chaincode/controller/operations.go index f983790..749ce6a 100644 --- a/chaincode/controller/operations.go +++ b/chaincode/controller/operations.go @@ -21,6 +21,7 @@ import ( "git.code.tecnalia.com/traceblock/sdk/middleware" "git.code.tecnalia.com/blockchain/hypercog/controller/bid" + "git.code.tecnalia.com/blockchain/hypercog/controller/stats" m2 "git.code.tecnalia.com/blockchain/hypercog/middleware" ) @@ -79,7 +80,9 @@ func ContextOperations(m shared.AbstractChaincodeOperationManager) error { middleware.MarkAssetAsCreated, middleware.InjectAssetOwnerData, }, - nil, + []shared.MiddlewareInterface{ + m2.UpdateStats, + }, assetController.SaveAbstractAsset, ). AddOperation( @@ -192,6 +195,15 @@ func ContextOperations(m shared.AbstractChaincodeOperationManager) error { AddBasicReadOperation("random-uuid", testController.DistributedPrngUuid). AddBasicReadOperation("random-string", testController.DistributedPrngString). // HyperCOG operations + AddOperation( + "hypercog-stats", + shared.READ_OP, + []shared.MiddlewareInterface{ + m2.RejectIfNotPublicAdmin, + }, + nil, + stats.GetStatsOperation, + ). AddOperation( "hypercog-list-bid-assets", shared.READ_OP, @@ -206,7 +218,7 @@ func ContextOperations(m shared.AbstractChaincodeOperationManager) error { AddOperationWithoutDefaults( "hypercog-bid-asset", shared.WRITE_OP, - &assetController, + &assetController, // Not used, just because it is required bidController.StubReader, bidController.LedgerReader, []shared.MiddlewareInterface{ @@ -218,7 +230,7 @@ func ContextOperations(m shared.AbstractChaincodeOperationManager) error { AddOperationWithoutDefaults( "hypercog-reject-asset", shared.WRITE_OP, - &assetController, + &assetController, // Not used, just because it is required bidDecisionController.StubReader, bidDecisionController.LedgerReader, []shared.MiddlewareInterface{ @@ -230,7 +242,7 @@ func ContextOperations(m shared.AbstractChaincodeOperationManager) error { AddOperationWithoutDefaults( "hypercog-accept-asset", shared.WRITE_OP, - &assetController, + &assetController, // Not used, just because it is required bidDecisionController.StubReader, bidDecisionController.LedgerReader, []shared.MiddlewareInterface{ diff --git a/chaincode/controller/stats/stats_controller.go b/chaincode/controller/stats/stats_controller.go new file mode 100644 index 0000000..335d88d --- /dev/null +++ b/chaincode/controller/stats/stats_controller.go @@ -0,0 +1,51 @@ +/** + * stats_controller.go + * + * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022. + */ + +package stats + +import ( + "git.code.tecnalia.com/blockchain/hypercog/model" + "git.code.tecnalia.com/ledgerbuilder/sdk/core/api" + "git.code.tecnalia.com/ledgerbuilder/sdk/core/fabric/protos" + "git.code.tecnalia.com/ledgerbuilder/sdk/shared" +) + + +type StatsResult struct { + GlobalStats model.Stats `json:"global"` // in tons + PerCompany map[string]model.Stats `json:"perCompany,omitempty"` +} + +func GetStatsOperation(stub shared.LedgerBuildrStubInterface, request shared.LedgerBuildrAsset) protos.Response { + fnName := "GetStats" + + ret := new(StatsResult) + + orgs, err := getOrgList(stub) + if err != nil { + return api.NewApiResponsePtr(fnName, err, nil).SendResponse() + } + + globalStat, err := GetStats(stub, GLOBAL_KEY) + if err != nil { + return api.NewApiResponsePtr(fnName, err, nil).SendResponse() + } + ret.GlobalStats = *globalStat + + if len(*orgs) > 0 { + ret.PerCompany = make(map[string]model.Stats) + } + + for _, org := range *orgs { + orgStat, err := GetStats(stub, org) + if err != nil { + return api.NewApiResponsePtr(fnName, err, nil).SendResponse() + } + ret.PerCompany[org] = *orgStat + } + + return api.NewAPIGenericResponsePtr(fnName, nil, ret).SendResponse() +} diff --git a/chaincode/controller/stats/stub.go b/chaincode/controller/stats/stub.go new file mode 100644 index 0000000..f3763d4 --- /dev/null +++ b/chaincode/controller/stats/stub.go @@ -0,0 +1,94 @@ +/** + * stub.go + * + * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022. + */ + +package stats + +import ( + "encoding/json" + + "git.code.tecnalia.com/blockchain/hypercog/model" + "git.code.tecnalia.com/ledgerbuilder/sdk/shared" +) + +const GLOBAL_KEY = "global" +const ORG_LIST_KEY = "org_list" + +type OrgList []string + + +func GetStats(stub shared.LedgerBuildrStubInterface, key string) (st *model.Stats, err error) { + value, err := stub.GetState(key) + if err != nil || len(value) == 0 { + st = new(model.Stats) + } else { + err = json.Unmarshal(value, &st) + if err != nil { + return nil, err + } + } + + return st, nil +} + +func PutStats(stub shared.LedgerBuildrStubInterface, key string, stats *model.Stats) (error) { + serialized, err := json.Marshal(stats) + if err != nil { + return err + } + + err = stub.PutState(key, serialized) + if err != nil { + return err + } + + return nil +} + +func getOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) { + value, err := stub.GetState(ORG_LIST_KEY) + if err != nil || len(value) == 0 { + st = new(OrgList) + } else { + err = json.Unmarshal(value, &st) + if err != nil { + return nil, err + } + } + + return st, nil +} + +func (orgs OrgList) contains(org string) (bool) { + for _, o := range orgs { + if o == org { + return true + } + } + return false +} + +func AddOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err error) { + orgs, err := getOrgList(stub) + if err != nil { + return err + } + + if !orgs.contains(org) { + newOrgs := append(*orgs, org) + + serialized, err := json.Marshal(newOrgs) + if err != nil { + return err + } + + err = stub.PutState(ORG_LIST_KEY, serialized) + if err != nil { + return err + } + } + + return nil +} \ No newline at end of file diff --git a/chaincode/main.go b/chaincode/main.go index 9b6db41..a7bc19e 100644 --- a/chaincode/main.go +++ b/chaincode/main.go @@ -18,7 +18,7 @@ import ( const ( defaultContractConfig = `{ "context_id": 0, - "chaincode_name": "traceblock-cc", + "chaincode_name": "hypercog-cc", "features": { "force_lowercase_ids": true, "generate_abi": false, diff --git a/chaincode/middleware/acl.go b/chaincode/middleware/acl.go index 2c8898c..6101957 100644 --- a/chaincode/middleware/acl.go +++ b/chaincode/middleware/acl.go @@ -14,6 +14,7 @@ import ( var ( RejectIfNotSidenor = shared.NewMiddlewareFunction("reject-not-sidenor", rejectIfNotSidenor) RejectIfNotCementCompany = shared.NewMiddlewareFunction("reject-not-cement-company", rejectIfNotCementCompany) + RejectIfNotPublicAdmin = shared.NewMiddlewareFunction("reject-not-public-admin", rejectIfNotPublicAdmin) errInvalidOrg = errors.New("user belongs to an invalid organization") ) @@ -34,5 +35,15 @@ func rejectIfNotCementCompany(stub shared.LedgerBuildrStubInterface, ctl shared. return request, nil } + return nil, errInvalidOrg +} + +func rejectIfNotPublicAdmin(stub shared.LedgerBuildrStubInterface, ctl shared.ControllerInterface, req shared.TXRequestInterface, request shared.LedgerBuildrAsset) (shared.LedgerBuildrAsset, error) { + mspId := stub.GetMSPId() + + if (mspId == "public-administration-com") { + return request, nil + } + return nil, errInvalidOrg } \ No newline at end of file diff --git a/chaincode/middleware/stats.go b/chaincode/middleware/stats.go new file mode 100644 index 0000000..d012f69 --- /dev/null +++ b/chaincode/middleware/stats.go @@ -0,0 +1,92 @@ +/** + * type.go + * + * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022. + */ + +package middleware + +import ( + "git.code.tecnalia.com/blockchain/hypercog/controller/stats" + "git.code.tecnalia.com/blockchain/hypercog/model" + "git.code.tecnalia.com/ledgerbuilder/sdk/shared" + "git.code.tecnalia.com/traceblock/sdk/middleware" +) + +var ( + UpdateStats = shared.NewMiddlewareFunction("update-stats-slag", _updateSlagProduction) +) + + +func _retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*model.Stats, *model.Stats, error) { + org, err := stub.GetOrganization() + if err != nil { + return nil, nil, err + } + + customStats, err := stats.GetStats(stub, org) + if err != nil { + return nil, nil, err + } + + globalStats, err := stats.GetStats(stub , stats.GLOBAL_KEY) + if err != nil { + return nil, nil, err + } + + return globalStats, customStats, nil +} + +func _upgradeStats(stub shared.LedgerBuildrStubInterface, globalStats *model.Stats, customStats *model.Stats) (error) { + org, err := stub.GetOrganization() + if err != nil { + return err + } + + err = stats.PutStats(stub, org, customStats) + if err != nil { + return err + } + + err = stats.PutStats(stub, stats.GLOBAL_KEY, globalStats) + if err != nil { + return err + } + + err = stats.AddOrgIfNotExist(stub, org) + if err != nil { + return err + } + + return nil +} + +func _updateSlagProduction(stub shared.LedgerBuildrStubInterface, ctl shared.ControllerInterface, req shared.TXRequestInterface, requestAsset shared.LedgerBuildrAsset) (shared.LedgerBuildrAsset, error) { + asst, err := middleware.ConvertToTraceableAsset(requestAsset) + if err != nil { + return nil, err + } + + status, ok := asst.Get("status") + if ok && status == "slag" { + globalStats, customStats, err := _retrieveExistingStats(stub) + if err != nil { + return nil, err + } + + amount := float64(asst.Quantity) + if asst.Units == "kg" { + amount /= 1000.0 + } + + customStats.TotalSlag += amount + globalStats.TotalSlag += amount + + err = _upgradeStats(stub, globalStats, customStats) + if err != nil { + return nil, err + } + } + + return asst, nil +} \ No newline at end of file diff --git a/chaincode/model/stats.go b/chaincode/model/stats.go new file mode 100644 index 0000000..d6d1f36 --- /dev/null +++ b/chaincode/model/stats.go @@ -0,0 +1,17 @@ +/** + * stats.go + * + * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022. + */ + +package model + +// Total or per company +type Stats struct { + TotalSlag float64 `json:"total"` // in tons + SlagReused float64 `json:"reused"` // in tons + SlagDiscarded float64 `json:"discarded"` // in tons + TotalPrice float64 `json:"price"` // in € + MinPrice float64 `json:"minPrice"` // in € + MaxPrice float64 `json:"maxPrice"` // in € +} -- GitLab