Fix transferFrom function so that we only delete token balances after we've verified the funds are available first - not before

Signed-off-by: Rob Evans <robevansuk@noreply.users.github.io>
This commit is contained in:
Rob Evans 2023-06-22 00:36:08 +01:00
parent 7671bdd7c6
commit 074c5dc237

View file

@ -9,6 +9,7 @@ package chaincode
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -949,6 +950,8 @@ func removeBalance(ctx contractapi.TransactionContextInterface, sender string, i
} }
defer balanceIterator.Close() defer balanceIterator.Close()
var deferredDeletions = []func() error{}
// Iterate over keys that store balances and add them to partialBalance until // Iterate over keys that store balances and add them to partialBalance until
// either the necessary amount is reached or the keys ended // either the necessary amount is reached or the keys ended
for balanceIterator.HasNext() && partialBalance < neededAmount { for balanceIterator.HasNext() && partialBalance < neededAmount {
@ -972,40 +975,47 @@ func removeBalance(ctx contractapi.TransactionContextInterface, sender string, i
selfRecipientKeyNeedsToBeRemoved = true selfRecipientKeyNeedsToBeRemoved = true
selfRecipientKey = queryResponse.Key selfRecipientKey = queryResponse.Key
} else { } else {
err = ctx.GetStub().DelState(queryResponse.Key) deferredDeletions = append(deferredDeletions, deferredDelete(ctx, queryResponse))
if err != nil {
return fmt.Errorf("failed to delete the state of %v: %v", queryResponse.Key, err)
}
} }
} }
if partialBalance < neededAmount { if partialBalance < neededAmount {
return fmt.Errorf("sender has insufficient funds for token %v, needed funds: %v, available fund: %v", tokenId, neededAmount, partialBalance) return fmt.Errorf("sender has insufficient funds for token %v, needed funds: %v, available fund: %v", tokenId, neededAmount, partialBalance)
} else if partialBalance > neededAmount {
// Send the remainder back to the sender
remainder, err := sub(partialBalance, neededAmount)
if err != nil {
return err
}
if selfRecipientKeyNeedsToBeRemoved {
// Set balance for the key that has the same address for sender and recipient
err = setBalance(ctx, sender, sender, tokenId, remainder)
if err != nil {
return err
}
} else {
err = addBalance(ctx, sender, sender, tokenId, remainder)
if err != nil {
return err
}
}
} else { } else {
// Delete self recipient key // enough token funds have been found to perform the update
err = ctx.GetStub().DelState(selfRecipientKey) // now we can delete the token entries to supply updated token balances
if err != nil { for _, deleteFn := range deferredDeletions {
return fmt.Errorf("failed to delete the state of %v: %v", selfRecipientKey, err) err := deleteFn()
if err != nil {
return err
}
}
if partialBalance > neededAmount {
// Send the remainder back to the sender
remainder, err := sub(partialBalance, neededAmount)
if err != nil {
return err
}
if selfRecipientKeyNeedsToBeRemoved {
// Set balance for the key that has the same address for sender and recipient
err = setBalance(ctx, sender, sender, tokenId, remainder)
if err != nil {
return err
}
} else {
err = addBalance(ctx, sender, sender, tokenId, remainder)
if err != nil {
return err
}
}
} else {
// Delete self recipient key
err = ctx.GetStub().DelState(selfRecipientKey)
if err != nil {
return fmt.Errorf("failed to delete the state of %v: %v", selfRecipientKey, err)
}
} }
} }
} }
@ -1013,6 +1023,16 @@ func removeBalance(ctx contractapi.TransactionContextInterface, sender string, i
return nil return nil
} }
func deferredDelete(ctx contractapi.TransactionContextInterface, queryResponse *queryresult.KV) func() error {
return func() error {
err := ctx.GetStub().DelState(queryResponse.Key)
if err != nil {
return fmt.Errorf("failed to delete the state of %v: %v", queryResponse.Key, err)
}
return nil
}
}
func emitTransferSingle(ctx contractapi.TransactionContextInterface, transferSingleEvent TransferSingle) error { func emitTransferSingle(ctx contractapi.TransactionContextInterface, transferSingleEvent TransferSingle) error {
transferSingleEventJSON, err := json.Marshal(transferSingleEvent) transferSingleEventJSON, err := json.Marshal(transferSingleEvent)
if err != nil { if err != nil {