mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
Merge cdb64fb0bb into a2c40e6522
This commit is contained in:
commit
96b1d66f13
4 changed files with 175 additions and 22 deletions
83
fabric-samples-main/token-sdk/auditor/service/audit.go
Normal file
83
fabric-samples-main/token-sdk/auditor/service/audit.go
Normal file
|
|
@ -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
fabric-samples-main/token-sdk/owner/service/balance.go
Normal file
73
fabric-samples-main/token-sdk/owner/service/balance.go
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
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{}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue