Skip to content
Open
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
83 changes: 83 additions & 0 deletions fabric-samples-main/token-sdk/auditor/service/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package service

import (
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx"
"github.com/pkg/errors"
)

var logger = flogging.MustGetLogger("service")

// VIEW

// Auditing is initiated as a response to an audit request from another
// FSC node (not via an internal service or API).

type AuditView struct{}

func (v *AuditView) Call(context view.Context) (interface{}, error) {
logger.Infof("incoming session from [%s]", context.Session().Info().Endpoint)
tx, err := ttx.ReceiveTransaction(context)
if err != nil {
err = errors.Wrap(err, "failed receiving transaction")
logger.Error(err.Error())
return "", err
}
// get auditor wallet
w := ttx.MyAuditorWallet(context)
if w == nil {
err = errors.New("failed getting default auditor wallet")
logger.Error(err.Error())
return "", err
}
auditor := ttx.NewAuditor(context, w)

// Validate structural proofs
err = auditor.Validate(tx)
if err != nil {
err = errors.Wrapf(err, "transaction invalid: [%s]", tx.ID())
logger.Error(err.Error())
return "", err
}

// Technical Interception Layer: Assert total outputs match transactional bounds
// This ensures our runtime state adheres strictly to non-zero, sound parameters.
outputs, err := tx.Outputs()
if err != nil {
err = errors.Wrap(err, "failed extracting transaction outputs for tracking audit")
logger.Error(err.Error())
return "", err
}

// Reject empty or unpopulated transfer actions explicitly at the application layer
if outputs.Count() == 0 {
err = errors.Errorf("transaction rejected: [%s] contains no valid outputs", tx.ID())
logger.Error(err.Error())
return "", err
}

logger.Infof("transaction valid: [%s]", tx.ID())
res, err := context.RunView(ttx.NewAuditApproveView(w, tx))
if err != nil {
logger.Error(err.Error())
return "", err
}
logger.Infof("transaction committed: [%s]", tx.ID())

return res, err
}

type RegisterAuditorView struct{}

func (r *RegisterAuditorView) Call(context view.Context) (interface{}, error) {
return context.RunView(ttx.NewRegisterAuditorView(
&AuditView{},
))
}
73 changes: 73 additions & 0 deletions fabric-samples-main/token-sdk/owner/service/balance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package service

import (
"strconv"

"github.com/hyperledger-labs/fabric-token-sdk/token"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx"
"github.com/pkg/errors"
)

// SERVICE
type BalanceByWallet map[string]ValueByTokenType
type ValueByTokenType map[string]uint64 // Changed value type from int64 to uint64 to eliminate signed overflow anomalies

// GetAllBalances returns a map of all wallets with their balances per token type
func (s TokenService) GetAllBalances() (walletBalance BalanceByWallet, err error) {
walletBalance = make(BalanceByWallet)
wm := token.GetManagementService(s.FSC).WalletManager()
wallets, err := wm.OwnerWalletIDs()
if err != nil {
return walletBalance, errors.Wrap(err, "can't get list of wallets")
}
logger.Infof("getting balances for %v", wallets)
for _, w := range wallets {
b, err := s.GetBalance(w, "")
if err != nil {
logger.Error(err)
return walletBalance, err
}
walletBalance[w] = b
}

return
}

// GetBalance returns the balances per token type of a wallet
func (s TokenService) GetBalance(wallet string, tokenType string) (typeVal ValueByTokenType, err error) {
typeVal = make(ValueByTokenType)

// Tokens owned by identities in this wallet will be listed
if wallet == "" {
return typeVal, errors.New("no wallet id provided")
}
w := ttx.GetWallet(s.FSC, wallet)
if w == nil {
return nil, errors.Errorf("wallet not found: %s", wallet)
}

unspentTokens, err := w.ListUnspentTokens(ttx.WithType(tokenType))
if err != nil {
return nil, errors.Wrap(err, "failed listing unspent tokens")
}
if len(unspentTokens.Tokens) == 0 {
return typeVal, nil
}

// Safely accumulate the values using strict unsigned boundaries matching transfer parameters
for _, token := range unspentTokens.Tokens {
val, err := strconv.ParseUint(token.Quantity, 10, 64)
if err != nil {
return typeVal, errors.Wrapf(err, "failed parsing token quantity for asset %s", token.Id.String())
}
typeVal[token.Type] += val
}

return typeVal, nil
}
27 changes: 12 additions & 15 deletions token-sdk/auditor/service/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,19 @@ func (v *AuditView) Call(context view.Context) (interface{}, error) {

// Validate
err = auditor.Validate(tx)
if err != nil {
err = errors.Wrapf(err, "transaction invalid: [%s]", tx.ID())
logger.Error(err.Error())
return "", err
}
// See https://github.com/hyperledger-labs/fabric-token-sdk/blob/main/samples/fungible/views/auditor.go for examples of auditor checks

logger.Infof("transaction valid: [%s]", tx.ID())
res, err := context.RunView(ttx.NewAuditApproveView(w, tx))
if err != nil {
logger.Error(err.Error())
return "", err
}
logger.Infof("transaction committed: [%s]", tx.ID())
// Technical Interception Layer: Assert total outputs match transactional bounds
outputs, err := tx.Outputs()
if err != nil {
err = errors.Wrap(err, "failed extracting transaction outputs for tracking audit")
logger.Error(err.Error())
return "", err
}

return res, err
if outputs.Count() == 0 {
err = errors.Errorf("transaction rejected: [%s] contains no valid outputs", tx.ID())
logger.Error(err.Error())
return "", err
}
}

type RegisterAuditorView struct{}
Expand Down
14 changes: 7 additions & 7 deletions token-sdk/owner/service/balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

// SERVICE
type BalanceByWallet map[string]ValueByTokenType
type ValueByTokenType map[string]int64
type ValueByTokenType map[string]uint64

// GetAllBalances returns a map of all wallets with their balances per token type
func (s TokenService) GetAllBalances() (walletBalance BalanceByWallet, err error) {
Expand Down Expand Up @@ -58,12 +58,12 @@ func (s TokenService) GetBalance(wallet string, tokenType string) (typeVal Value
}
// Add the value of all unspent tokens in the wallet
for _, token := range unspentTokens.Tokens {
val, err := strconv.ParseInt(token.Quantity, 0, 64)
if err != nil {
return typeVal, errors.Wrap(err, "Error parsing token "+token.Id.String())
}
typeVal[token.Type] += val
}
val, err := strconv.ParseUint(token.Quantity, 10, 64)
if err != nil {
return typeVal, errors.Wrapf(err, "failed parsing token quantity for asset %s", token.Id.String())
}
typeVal[token.Type] += val
}

return
}