Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transaction to delete assets recursively #1

Merged
merged 1 commit into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions assets/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package assets

import (
"encoding/json"
"strings"

"github.com/goledgerdev/cc-tools/errors"
"github.com/hyperledger/fabric/core/chaincode/shim"
Expand Down Expand Up @@ -59,3 +60,88 @@ func (a *Asset) Delete(stub shim.ChaincodeStubInterface) ([]byte, error) {

return assetJSON, nil
}

// DeleteRecursive erases asset and recursively erases those which reference it
func (a *Asset) DeleteRecursive(stub shim.ChaincodeStubInterface) ([]byte, error) {
var err error

// Check if org has write permission
err = a.CheckWriters(stub)
if err != nil {
return nil, errors.WrapError(err, "failed write permission check")
}

err = deleteRecursive(stub, a.Key())
if err != nil {
return nil, errors.WrapError(err, "error deleting asset Recursively")
}

response := map[string]interface{}{
"deletedKeys": deletedKeys,
}
responseJSON, err := json.Marshal(response)
if err != nil {
return nil, errors.WrapError(err, "failed to marshal response")
}

return responseJSON, nil
}

var deletedKeys []string

func deleteRecursive(stub shim.ChaincodeStubInterface, key string) error {
deletedKeys = append(deletedKeys, key)
queryIt, err := stub.GetStateByPartialCompositeKey(key, []string{})
if err != nil {
return errors.WrapErrorWithStatus(err, "failed to check reference index", 500)
}
defer queryIt.Close()

for queryIt.HasNext() {
next, _ := queryIt.Next()
referrerKeyString := strings.ReplaceAll(next.Key, key, "")
referrerKeyString = strings.ReplaceAll(referrerKeyString, "\x00", "")
var isDeleted bool

for _, deletedKey := range deletedKeys {
if deletedKey == referrerKeyString {
isDeleted = true
break
}
}
if !isDeleted {
err = deleteRecursive(stub, referrerKeyString)
if err != nil {
return errors.WrapError(err, "error deleting referrer asset:")
}
}
}

keyMap := make(map[string]interface{})
keyMap["@key"] = key
assetKey, err := NewKey(keyMap)

asset, err := assetKey.Get(stub)
if err != nil {
return errors.WrapError(err, "failed to read asset from blockchain")
}
// Clean up reference markers for this asset
err = asset.delRefs(stub)
if err != nil {
return errors.WrapError(err, "failed cleaning reference index")
}

if !asset.IsPrivate() {
err = stub.DelState(asset.Key())
if err != nil {
return errors.WrapError(err, "failed to delete state from ledger")
}
} else {
err = stub.DelPrivateData(asset.TypeTag(), asset.Key())
if err != nil {
return errors.WrapError(err, "failed to delete state from private collection")
}
}

return nil
}
3 changes: 3 additions & 0 deletions assets/references.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ func (a Asset) delRefs(stub shim.ChaincodeStubInterface) errors.ICCError {
for _, referencedKey := range refKeys {
// Construct reference key
indexKey, err := stub.CreateCompositeKey(referencedKey.Key(), []string{assetKey})
if err != nil {
return errors.WrapErrorWithStatus(err, "could not create composite key", 400)
}
// Check if asset exists in blockchain
err = stub.DelState(indexKey)
if err != nil {
Expand Down
37 changes: 37 additions & 0 deletions transactions/deleteAsset.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,40 @@ var DeleteAsset = Transaction{
return response, nil
},
}

// DeleteRecursive deletes an asset and it's recursive referrers from the blockchain
var DeleteRecursive = Transaction{
Tag: "deleteAssetRecursive",
Label: "Delete Asset",
Method: "DELETE",
Description: "",

MetaTx: true,
Args: []Argument{
{
Tag: "key",
Description: "Key of the asset to be deleted.",
DataType: "@key",
Required: true,
},
},
Routine: func(stub shim.ChaincodeStubInterface, req map[string]interface{}) ([]byte, errors.ICCError) {
// This is safe to do because validation is done before calling routine
key := req["key"].(assets.Key)

var err error

// Fetch asset from blockchain
asset, err := key.Get(stub)
if err != nil {
return nil, errors.WrapError(err, "failed to read asset from blockchain")
}

response, err := asset.DeleteRecursive(stub)
if err != nil {
return nil, errors.WrapError(err, "failed to delete asset recursively")
}

return response, nil
},
}
1 change: 1 addition & 0 deletions transactions/txList.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var basicTxs = []Transaction{
Search,
UpdateAsset,
DeleteAsset,
DeleteRecursive,
}

// TxList returns a copy of the txList variable
Expand Down