Skip to content

Commit

Permalink
Merge pull request #2 from GoLedgerDev/fix/deleteRecursive
Browse files Browse the repository at this point in the history
Fix/delete recursive
  • Loading branch information
bandreghetti authored Jun 11, 2021
2 parents 47f55c1 + 50fa7c6 commit 3ee98a1
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 83 deletions.
85 changes: 40 additions & 45 deletions assets/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,9 @@ import (
sw "github.com/goledgerdev/cc-tools/stubwrapper"
)

// Delete erases asset from world state and checks for all necessary permissions.
// An asset cannot be deleted if any other asset references it.
func (a *Asset) Delete(stub *sw.StubWrapper) ([]byte, errors.ICCError) {
func (a *Asset) delete(stub *sw.StubWrapper) ([]byte, errors.ICCError) {
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")
}

// Check if asset is referenced in other assets to avoid data inconsistency
isReferenced, err := a.IsReferenced(stub)
if err != nil {
return nil, errors.WrapError(err, "failed to check if asset if being referenced")
}
if isReferenced {
return nil, errors.NewCCError("another asset holds a reference to this one", 400)
}

// Clean up reference markers for this asset
err = a.delRefs(stub)
if err != nil {
Expand Down Expand Up @@ -62,8 +45,29 @@ func (a *Asset) Delete(stub *sw.StubWrapper) ([]byte, errors.ICCError) {
return assetJSON, nil
}

// Delete erases asset from world state and checks for all necessary permissions.
// An asset cannot be deleted if any other asset references it.
func (a *Asset) Delete(stub *sw.StubWrapper) ([]byte, errors.ICCError) {
// Check if org has write permission
err := a.CheckWriters(stub)
if err != nil {
return nil, errors.WrapError(err, "failed write permission check")
}

// Check if asset is referenced in other assets to avoid data inconsistency
isReferenced, err := a.IsReferenced(stub)
if err != nil {
return nil, errors.WrapError(err, "failed to check if asset if being referenced")
}
if isReferenced {
return nil, errors.NewCCError("another asset holds a reference to this one", 400)
}

return a.delete(stub)
}

// DeleteRecursive erases asset and recursively erases those which reference it
func (a *Asset) DeleteRecursive(stub *sw.StubWrapper) ([]byte, errors.ICCError) {
func (a *Asset) DeleteCascade(stub *sw.StubWrapper) ([]byte, errors.ICCError) {
var err error

// Check if org has write permission
Expand All @@ -72,7 +76,8 @@ func (a *Asset) DeleteRecursive(stub *sw.StubWrapper) ([]byte, errors.ICCError)
return nil, errors.WrapError(err, "failed write permission check")
}

err = deleteRecursive(stub, a.Key())
deletedKeys := make([]string, 0)
err = deleteRecursive(stub, a.Key(), &deletedKeys)
if err != nil {
return nil, errors.WrapError(err, "error deleting asset Recursively")
}
Expand All @@ -88,34 +93,36 @@ func (a *Asset) DeleteRecursive(stub *sw.StubWrapper) ([]byte, errors.ICCError)
return responseJSON, nil
}

var deletedKeys []string
func deleteRecursive(stub *sw.StubWrapper, key string, deletedKeys *[]string) errors.ICCError {

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

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

for _, deletedKey := range *deletedKeys {

for _, deletedKey := range deletedKeys {
if deletedKey == referrerKeyString {
isDeleted = true
break
}
}
*deletedKeys = append(*deletedKeys, referrerKeyString)

if !isDeleted {
err = deleteRecursive(stub, referrerKeyString)
err = deleteRecursive(stub, referrerKeyString, deletedKeys)
if err != nil {
return errors.WrapError(err, "error deleting referrer asset")
}
}

}

keyMap := make(map[string]interface{})
Expand All @@ -129,22 +136,10 @@ func deleteRecursive(stub *sw.StubWrapper, key string) errors.ICCError {
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")
}
_, err = asset.delete(stub)
if err != nil {
return errors.WrapError(err, "failed to delete asset")
}

return nil
Expand Down
132 changes: 132 additions & 0 deletions assets/delete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package assets

import (
"encoding/json"
"fmt"
"testing"

sw "github.com/goledgerdev/cc-tools/stubwrapper"
"github.com/hyperledger/fabric/core/chaincode/shim"
)

func TestDeleteAsset(t *testing.T) {
stub := shim.NewMockStub("testcc", new(testCC))

stub.MockTransactionStart("TestDeleteAsset")
var a Asset
assetJSON := []byte("{\"@assetType\": \"samplePerson\",\"name\": \"Maria\",\"cpf\": \"318.207.920-48\",\"readerScore\": 70}")
err := json.Unmarshal(assetJSON, &a)
if err != nil {
fmt.Println(err)
t.FailNow()
}
sw := &sw.StubWrapper{
Stub: stub,
}
_, err = a.put(sw)
if err != nil {
fmt.Println(err)
t.FailNow()
}
stub.MockTransactionEnd("TestDeleteAsset")

stub.MockTransactionStart("TestDeleteAsset")

a.delete(sw)
if err != nil {
fmt.Println(err)
t.FailNow()
}

stub.MockTransactionEnd("TestDeleteAsset")

stub.MockTransactionStart("TestDeleteAsset")

res, err := a.Get(sw)
if err == nil {
fmt.Println("should not be capable of getting asset", res)
t.FailNow()
}

stub.MockTransactionEnd("TestDeleteAsset")
}

func TestDeleteAssetCascade(t *testing.T) {
stub := shim.NewMockStub("testcc", new(testCC))

stub.MockTransactionStart("TestDeleteAssetCascade")
var personAsset Asset
personJSON := []byte("{\"@assetType\": \"samplePerson\",\"name\": \"Maria\",\"cpf\": \"318.207.920-48\",\"readerScore\": 70}")
err := json.Unmarshal(personJSON, &personAsset)
if err != nil {
fmt.Println(err)
t.FailNow()
}
sw := &sw.StubWrapper{
Stub: stub,
}
_, err = personAsset.put(sw)
if err != nil {
fmt.Println(err)
t.FailNow()
}
stub.MockTransactionEnd("TestDeleteAssetCascade")

stub.MockTransactionStart("TestDeleteAssetCascade")
tenant, err := NewKey(personAsset)
bookString := fmt.Sprintf("{\"@assetType\": \"sampleBook\",\"title\": \"Pale Blue Dot\",\"author\": \"Carl Sagan\",\"currentTenant\": %s}", tenant)
if err != nil {
fmt.Println(err)
t.FailNow()
}
bookJSON := []byte(bookString)
var bookAsset Asset
err = json.Unmarshal(bookJSON, &bookAsset)
if err != nil {
fmt.Println(err)
t.FailNow()
}
_, err = bookAsset.put(sw)
if err != nil {
fmt.Println(err)
t.FailNow()
}
stub.MockTransactionEnd("TestDeleteAssetCascade")

stub.MockTransactionStart("TestDeleteAssetCascade")
var k Key
if err != nil {
fmt.Println(err)
t.FailNow()
}
keyJSON := []byte(bookString)
err = json.Unmarshal(keyJSON, &k)
if err != nil {
fmt.Println(err)
t.FailNow()
}

deletedKeys := make([]string, 0)
err = deleteRecursive(sw, personAsset.Key(), &deletedKeys)
if err != nil {
fmt.Println(err)
t.FailNow()
}
stub.MockTransactionEnd("TestDeleteAssetCascade")

stub.MockTransactionStart("TestGetAsset")

res, err := bookAsset.Get(sw)
if err == nil {
fmt.Println("should not be capable of getting asset", res)
t.FailNow()
}

res, err = personAsset.Get(sw)
if err == nil {
fmt.Println("should not be capable of getting asset", res)
t.FailNow()
}

stub.MockTransactionEnd("TestGetAsset")
}
1 change: 1 addition & 0 deletions assets/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func TestGetAsset(t *testing.T) {
fmt.Println(*gotAsset)
t.FailNow()
}
stub.MockTransactionEnd("TestGetAsset")
}

func TestGetRecursive(t *testing.T) {
Expand Down
57 changes: 19 additions & 38 deletions transactions/deleteAsset.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,47 +21,19 @@ var DeleteAsset = Transaction{
DataType: "@key",
Required: true,
},
},
Routine: func(stub *sw.StubWrapper, 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.Delete(stub)
if err != nil {
return nil, errors.WrapError(err, "failed to delete asset")
}

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,
Tag: "cascade",
Description: "Delete all referrers on cascade",
DataType: "boolean",
},
},
Routine: func(stub *sw.StubWrapper, req map[string]interface{}) ([]byte, errors.ICCError) {
// This is safe to do because validation is done before calling routine
key := req["key"].(assets.Key)
cascade, ok := req["cascade"].(bool)
if !ok {
cascade = false
}

var err error

Expand All @@ -71,9 +43,18 @@ var DeleteRecursive = Transaction{
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")
response := make([]byte, 0)
if cascade {
response, err = asset.DeleteCascade(stub)
if err != nil {
return nil, errors.WrapError(err, "failed to delete asset recursively")
}
} else {

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

return response, nil
Expand Down

0 comments on commit 3ee98a1

Please sign in to comment.