Skip to content

Commit

Permalink
checks and recovery functions (hyperledger-labs#792)
Browse files Browse the repository at this point in the history
* introduce ability to inject the checkers
* remove vault support form network

Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro authored Dec 26, 2024
1 parent 386c430 commit 2b04de8
Show file tree
Hide file tree
Showing 45 changed files with 582 additions and 525 deletions.
4 changes: 2 additions & 2 deletions integration/ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ var (
}

AllTestTypes = []*InfrastructureType{
// WebSocketNoReplication,
// LibP2PNoReplication,
WebSocketNoReplication,
LibP2PNoReplication,
WebSocketWithReplication,
}
)
Expand Down
3 changes: 1 addition & 2 deletions integration/token/fungible/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,7 @@ func TestAll(network *integration.Infrastructure, auditorId string, onRestart On
CheckBalanceAndHolding(network, bob, "", "EUR", 20, auditor)
CheckBalanceAndHolding(network, bob, "", "USD", 110, auditor)
CheckOwnerDB(network, []string{
fmt.Sprintf("transaction record [%s] is unknown for vault but not for the db [Pending]", txID1),
fmt.Sprintf("transaction record [%s] is unknown for vault but not for the db [Pending]", txID2),
//TODO: Errors
}, bob)
fmt.Printf("prepared transactions [%s:%s]", txID1, txID2)
Restart(network, true, onRestart, bob)
Expand Down
15 changes: 2 additions & 13 deletions integration/token/fungible/views/auditor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/kvs"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
"github.com/hyperledger-labs/fabric-token-sdk/token"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/network"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -156,7 +155,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) {

for _, rID := range inputs.RevocationHandles() {
rh := hash.Hashable(rID).String()
//logger.Infof("input RH [%s]", rh)
// logger.Infof("input RH [%s]", rh)
assert.NotNil(rID, "found an input with empty RH")
k := kvs.CreateCompositeKeyOrPanic("revocationList", []string{rh})
if kvsInstance.Exists(k) {
Expand All @@ -166,7 +165,7 @@ func (a *AuditView) Call(context view.Context) (interface{}, error) {

for _, rID := range outputs.RevocationHandles() {
rh := hash.Hashable(rID).String()
//logger.Infof("output RH [%s]", rh)
// logger.Infof("output RH [%s]", rh)
assert.NotNil(rID, "found an output with empty RH")
k := kvs.CreateCompositeKeyOrPanic("revocationList", []string{rh})
if kvsInstance.Exists(k) {
Expand Down Expand Up @@ -302,16 +301,6 @@ func (r *SetTransactionAuditStatusView) Call(context view.Context) (interface{},
assert.NoError(err, "failed to get auditor instance")
assert.NoError(auditor.SetStatus(context.Context(), r.TxID, r.Status, r.Message), "failed to set status of [%s] to [%d]", r.TxID, r.Status)

if r.Status == ttx.Deleted {
tms := token.GetManagementService(context)
assert.NotNil(tms, "failed to get default tms")
net := network.GetInstance(context, tms.Network(), tms.Channel())
assert.NotNil(net, "failed to get network [%s:%s]", tms.Network(), tms.Channel())
v, err := net.Vault(tms.Namespace())
assert.NoError(err)
assert.NoError(v.DiscardTx(r.TxID), "failed to discard tx [%s:%s:%s:%s]", tms.Network(), tms.Channel(), tms.Namespace(), r.TxID)
}

return nil, nil
}

Expand Down
200 changes: 12 additions & 188 deletions integration/token/fungible/views/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@ SPDX-License-Identifier: Apache-2.0
package views

import (
"bytes"
"encoding/json"
"fmt"

"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/assert"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
"github.com/hyperledger-labs/fabric-token-sdk/token"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/auditdb"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/db/driver"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/interop/htlc"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/network"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttxdb"
token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token"
"github.com/pkg/errors"
)
Expand All @@ -43,108 +38,7 @@ type CheckTTXDBView struct {
}

func (m *CheckTTXDBView) Call(context view.Context) (interface{}, error) {
var errorMessages []string

tms := token.GetManagementService(context, token.WithTMSID(m.TMSID))
assert.NotNil(tms, "failed to get default tms")
net := network.GetInstance(context, tms.Network(), tms.Channel())
assert.NotNil(net, "failed to get network [%s:%s]", tms.Network(), tms.Channel())
v, err := net.Vault(tms.Namespace())
assert.NoError(err, "failed to get vault [%s:%s:%s]", tms.Network(), tms.Channel(), tms.Namespace())
tv, err := net.TokenVault(tms.Namespace())
assert.NoError(err, "failed to get token vault [%s:%s:%s]", tms.Namespace(), tms.Channel(), tms.Namespace())
l, err := net.Ledger()
assert.NoError(err, "failed to get ledger [%s:%s:%s]", tms.Network(), tms.Channel(), tms.Namespace())

var tokenDB TokenTransactionDB
if m.Auditor {
auditorWallet := tms.WalletManager().AuditorWallet(m.AuditorWalletID)
assert.NotNil(auditorWallet, "cannot find auditor wallet [%s]", m.AuditorWalletID)
db, err := ttx.NewAuditor(context, auditorWallet)
assert.NoError(err, "failed to get auditor instance")
tokenDB = db
} else {
db := ttx.NewOwner(context, tms)
tokenDB = db
}
it, err := tokenDB.Transactions(driver.QueryTransactionsParams{})
assert.NoError(err, "failed to get transaction iterators")
defer it.Close()
for {
transactionRecord, err := it.Next()
assert.NoError(err, "failed to get next transaction record")
if transactionRecord == nil {
break
}

// compare the status in the vault with the status of the record
vc, _, err := v.Status(transactionRecord.TxID)
if err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("failed to get vault status transaction record [%s]: [%s]", transactionRecord.TxID, err))
continue
}
switch {
case vc == network.Unknown:
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is unknown for vault but not for the db [%s]", transactionRecord.TxID, driver.TxStatusMessage[transactionRecord.Status]))
case vc == network.Valid && transactionRecord.Status == ttxdb.Pending:
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is valid for vault but pending for the db", transactionRecord.TxID))
case vc == network.Valid && transactionRecord.Status == ttxdb.Deleted:
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is valid for vault but deleted for the db", transactionRecord.TxID))
case vc == network.Invalid && transactionRecord.Status == ttxdb.Confirmed:
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is invalid for vault but confirmed for the db", transactionRecord.TxID))
case vc == network.Invalid && transactionRecord.Status == ttxdb.Pending:
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is invalid for vault but pending for the db", transactionRecord.TxID))
case vc == network.Busy && transactionRecord.Status == ttxdb.Confirmed:
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is busy for vault but confirmed for the db", transactionRecord.TxID))
case vc == network.Busy && transactionRecord.Status == ttxdb.Deleted:
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is busy for vault but deleted for the db", transactionRecord.TxID))
}

// check envelope
//if !net.ExistEnvelope(transactionRecord.TxID) {
// errorMessages = append(errorMessages, fmt.Sprintf("no envelope found for transaction record [%s]", transactionRecord.TxID))
//}

tokenRequest, err := tokenDB.GetTokenRequest(transactionRecord.TxID)
assert.NoError(err, "failed to retrieve token request for [%s]", transactionRecord.TxID)
assert.NotNil(tokenRequest, "token requests must not be nil")

// check the ledger
lVC, _, err := l.Status(transactionRecord.TxID)
if err != nil {
lVC = network.Unknown
}
switch {
case vc == network.Valid && lVC != network.Valid:
if err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("failed to get ledger transaction status for [%s]: [%s]", transactionRecord.TxID, err))
}
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is valid for vault but not for the ledger [%d]", transactionRecord.TxID, lVC))
case vc == network.Invalid && lVC != network.Invalid:
if lVC != network.Unknown || transactionRecord.Status != ttxdb.Deleted {
if err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("failed to get ledger transaction status for [%s]: [%s]", transactionRecord.TxID, err))
}
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is invalid for vault but not for the ledger [%d]", transactionRecord.TxID, lVC))
}
case vc == network.Unknown && lVC != network.Unknown:
if err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("failed to get ledger transaction status for [%s]: [%s]", transactionRecord.TxID, err))
}
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is unknown for vault but not for the ledger [%d]", transactionRecord.TxID, lVC))
case vc == network.Busy && lVC == network.Busy:
// this is fine, let's continue
case vc == network.Busy && lVC != network.Unknown:
if err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("failed to get ledger transaction status for [%s]: [%s]", transactionRecord.TxID, err))
}
errorMessages = append(errorMessages, fmt.Sprintf("transaction record [%s] is busy for vault but not for the ledger [%d]", transactionRecord.TxID, lVC))
}
}

// Match unspent tokens with the ledger
// but first delete the claimed tokens
// TODO: check all owner wallets
// prepare
defaultOwnerWallet := htlc.GetWallet(context, "", token.WithTMSID(m.TMSID))
if defaultOwnerWallet != nil {
htlcWallet := htlc.Wallet(context, defaultOwnerWallet)
Expand All @@ -153,39 +47,18 @@ func (m *CheckTTXDBView) Call(context view.Context) (interface{}, error) {
assert.NoError(htlcWallet.DeleteExpiredReceivedTokens(context), "failed to delete expired received tokens")
}

// check unspent tokens
uit, err := tv.QueryEngine().UnspentTokensIterator()
assert.NoError(err, "failed to get unspent tokens")
defer uit.Close()
var unspentTokenIDs []*token2.ID
for {
tok, err := uit.Next()
assert.NoError(err, "failed to get next unspent token")
if tok == nil {
break
}
unspentTokenIDs = append(unspentTokenIDs, tok.Id)
}
ledgerTokenContent, err := net.QueryTokens(context, tms.Namespace(), unspentTokenIDs)
if err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("failed to query tokens: [%s]", err))
} else {
assert.Equal(len(unspentTokenIDs), len(ledgerTokenContent))
index := 0
assert.NoError(tv.QueryEngine().GetTokenOutputs(unspentTokenIDs, func(id *token2.ID, tokenRaw []byte) error {
for _, content := range ledgerTokenContent {
if bytes.Equal(content, tokenRaw) {
return nil
}
}

errorMessages = append(errorMessages, fmt.Sprintf("token content does not match at [%s][%d], [%s]", id, index, hash.Hashable(tokenRaw)))
index++
return nil
}), "failed to match ledger token content with local")
// check
tms := token.GetManagementService(context, token.WithTMSID(m.TMSID))
assert.NotNil(tms, "failed to get default tms")
if m.Auditor {
auditorWallet := tms.WalletManager().AuditorWallet(m.AuditorWalletID)
assert.NotNil(auditorWallet, "cannot find auditor wallet [%s]", m.AuditorWalletID)
db, err := ttx.NewAuditor(context, auditorWallet)
assert.NoError(err, "failed to get auditor instance")
return db.Check(context.Context())
}

return errorMessages, nil
db := ttx.NewOwner(context, tms)
return db.Check(context.Context())
}

type CheckTTXDBViewFactory struct{}
Expand Down Expand Up @@ -291,52 +164,3 @@ func (c *CheckIfExistsInVaultViewFactory) NewView(in []byte) (view.View, error)

return f, nil
}

type TransactionRecord struct {
TxID string
Status driver.TxStatus
}

type AuditDBTransactionIterator struct {
*auditdb.TransactionIterator
}

func (t *AuditDBTransactionIterator) Close() {
t.TransactionIterator.Close()
}

func (t *AuditDBTransactionIterator) Next() (*TransactionRecord, error) {
next, err := t.TransactionIterator.Next()
if err != nil {
return nil, err
}
if next == nil {
return nil, nil
}
return &TransactionRecord{
TxID: next.TxID,
Status: next.Status,
}, nil
}

type TTXDBTransactionIterator struct {
*ttxdb.TransactionIterator
}

func (t *TTXDBTransactionIterator) Close() {
t.TransactionIterator.Close()
}

func (t *TTXDBTransactionIterator) Next() (*TransactionRecord, error) {
next, err := t.TransactionIterator.Next()
if err != nil {
return nil, err
}
if next == nil {
return nil, nil
}
return &TransactionRecord{
TxID: next.TxID,
Status: next.Status,
}, nil
}
11 changes: 0 additions & 11 deletions integration/token/fungible/views/owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/assert"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
"github.com/hyperledger-labs/fabric-token-sdk/token"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/network"
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx"
)

Expand All @@ -31,16 +30,6 @@ func (r *SetTransactionOwnerStatusView) Call(context view.Context) (interface{},
owner := ttx.NewOwner(context, token.GetManagementService(context))
assert.NoError(owner.SetStatus(context.Context(), r.TxID, r.Status, r.Message), "failed to set status of [%s] to [%d]", r.TxID, r.Status)

if r.Status == ttx.Deleted {
tms := token.GetManagementService(context)
assert.NotNil(tms, "failed to get default tms")
net := network.GetInstance(context, tms.Network(), tms.Channel())
assert.NotNil(net, "failed to get network [%s:%s]", tms.Network(), tms.Channel())
v, err := net.Vault(tms.Namespace())
assert.NoError(err, "failed to get vault [%s:%s:%s]", tms.Network(), tms.Channel(), tms.Namespace())
assert.NoError(v.DiscardTx(r.TxID), "failed to discard tx [%s:%s:%s:%s]", tms.Network(), tms.Channel(), tms.Namespace(), r.TxID)
}

return nil, nil
}

Expand Down
Loading

0 comments on commit 2b04de8

Please sign in to comment.