Skip to content

Commit

Permalink
clearly separate selection of tokens owned sololy by a single wallet …
Browse files Browse the repository at this point in the history
…from

selection of tokens whose ownership might be shared

Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro authored and alexandrosfilios committed Sep 16, 2024
1 parent 48eea97 commit 83744d4
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 75 deletions.
4 changes: 2 additions & 2 deletions token/core/common/authrorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
type Authorization interface {
// IsMine returns true if the passed token is owned by an owner wallet.
// It returns the ID of the owner wallet and any additional owner identifier, if supported.
// It is possible that the wallet ID is empty an the additional owner identifier list is not.
// It is possible that the wallet ID is empty and the additional owner identifier list is not.
IsMine(tok *token2.Token) (string, []string, bool)
// AmIAnAuditor return true if the passed TMS contains an auditor wallet for any of the auditor identities
// defined in the public parameters of the passed TMS.
Expand Down Expand Up @@ -44,7 +44,7 @@ func NewTMSAuthorization(publicParameters driver.PublicParameters, walletService
}

// IsMine returns true if the passed token is owned by an owner wallet.
// It returns the ID of the owner wallet and no additional owner identifiers
// It returns the ID of the owner wallet and no additional owner identifiers.
func (w *WalletBasedAuthorization) IsMine(tok *token2.Token) (string, []string, bool) {
wallet, err := w.WalletService.OwnerWallet(tok.Owner.Raw)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions token/driver/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ type UnspentTokensIterator interface {
Next() (*token.UnspentToken, error)
}

type MinTokenInfoIterator interface {
type SpendableTokensIterator interface {
Close()
Next() (*token.MinTokenInfo, error)
Next() (*token.UnspentTokenInWallet, error)
}

type Vault interface {
Expand Down Expand Up @@ -67,7 +67,7 @@ type QueryEngine interface {
UnspentTokensIterator() (UnspentTokensIterator, error)
// UnspentTokensIteratorBy returns an iterator of unspent tokens owned by the passed id and whose type is the passed on.
// The token type can be empty. In that case, tokens of any type are returned.
UnspentTokensIteratorBy(ctx context.Context, id, tokenType string) (UnspentTokensIterator, error)
UnspentTokensIteratorBy(ctx context.Context, walletID, tokenType string) (UnspentTokensIterator, error)
// ListUnspentTokens returns the list of unspent tokens
ListUnspentTokens() (*token.UnspentTokens, error)
// ListAuditTokens returns the audited tokens associated to the passed ids
Expand Down
8 changes: 5 additions & 3 deletions token/driver/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,11 @@ type WalletLookupID = any
// and wallets (owner, auditor, etc.)
type Authorization interface {
// IsMine returns true if the passed token is owned by an owner wallet.
// It returns the ID of the owner wallet and any additional owner identifier, if supported.
// It is possible that the wallet ID is empty an the additional owner identifier list is not.
IsMine(tok *token.Token) (string, []string, bool)
// It returns the ID of the owner wallet (walletID) and any additional owner identifier (additionalOwners), if supported.
// It is possible that walletID is empty additionalOwners is not.
// If walletID is not empty, this means that the corresponding wallet can spend the token directly.
// If walletID is empty, then additionalOwners must cooperate in some way in order to spend the token.
IsMine(tok *token.Token) (walletID string, additionalOwners []string, mine bool)
// AmIAnAuditor return true if the passed TMS contains an auditor wallet for any of the auditor identities
// defined in the public parameters of the passed TMS.
AmIAnAuditor() bool
Expand Down
10 changes: 5 additions & 5 deletions token/services/db/driver/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ type TokenDB interface {
IsMine(txID string, index uint64) (bool, error)
// UnspentTokensIterator returns an iterator over all owned tokens
UnspentTokensIterator() (driver.UnspentTokensIterator, error)
// UnspentTokensIteratorBy returns an iterator over all tokens owned by the passed identifier of a given type
UnspentTokensIteratorBy(ctx context.Context, id, tokenType string) (driver.UnspentTokensIterator, error)
// MinTokenInfoIteratorBy returns the minimum information about the tokens needed for the selector
MinTokenInfoIteratorBy(ctx context.Context, ownerEID string, typ string) (driver.MinTokenInfoIterator, error)
// UnspentTokensIteratorBy returns an iterator over all tokens owned by the passed wallet identifier and of a given type
UnspentTokensIteratorBy(ctx context.Context, walletID, tokenType string) (driver.UnspentTokensIterator, error)
// SpendableTokensIteratorBy returns an iterator over all tokens owned solely by the passed wallet identifier and of a given type
SpendableTokensIteratorBy(ctx context.Context, walletID string, typ string) (driver.SpendableTokensIterator, error)
// ListUnspentTokensBy returns the list of all tokens owned by the passed identifier of a given type
ListUnspentTokensBy(ownerEID, typ string) (*token.UnspentTokens, error)
ListUnspentTokensBy(walletID, typ string) (*token.UnspentTokens, error)
// ListUnspentTokens returns the list of all owned tokens
ListUnspentTokens() (*token.UnspentTokens, error)
// ListAuditTokens returns the audited tokens for the passed ids
Expand Down
6 changes: 3 additions & 3 deletions token/services/db/sql/common/querybuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ func tokenQuerySql(params driver.QueryTokenDetailsParams, tokenTable, ownerTable
}
if params.WalletID != "" {
args = append(args, params.WalletID)
and = append(and, fmt.Sprintf("wallet_id = $%d", len(args)))
and = append(and, fmt.Sprintf("(wallet_id = $%d OR owner_wallet_id = $%d)", len(args), len(args)+1))
args = append(args, params.WalletID)
}

if params.TokenType != "" {
Expand Down Expand Up @@ -200,8 +201,7 @@ func tokenQuerySqlNoJoin(params driver.QueryTokenDetailsParams) (where string, a
}

if len(params.TransactionIDs) > 0 {
colTxID := "tx_id"
and = append(and, in(&args, colTxID, params.TransactionIDs))
and = append(and, in(&args, "tx_id", params.TransactionIDs))
}
if ids := whereTokenIDs(&args, params.IDs); ids != "" {
and = append(and, ids)
Expand Down
24 changes: 12 additions & 12 deletions token/services/db/sql/common/querybuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,17 +255,17 @@ func TestTokenSql(t *testing.T) {
{
name: "owner unspent",
params: driver.QueryTokenDetailsParams{WalletID: "me"},
expectedSql: "WHERE owner = true AND wallet_id = $1 AND is_deleted = false",
expectedArgs: []interface{}{"me"},
expectedSql: "WHERE owner = true AND (wallet_id = $1 OR owner_wallet_id = $2) AND is_deleted = false",
expectedArgs: []interface{}{"me", "me"},
},
{
name: "owner with deleted",
params: driver.QueryTokenDetailsParams{
WalletID: "me",
IncludeDeleted: true,
},
expectedSql: "WHERE owner = true AND wallet_id = $1",
expectedArgs: []interface{}{"me"},
expectedSql: "WHERE owner = true AND (wallet_id = $1 OR owner_wallet_id = $2)",
expectedArgs: []interface{}{"me", "me"},
},
{
name: "owner and htlc with deleted",
Expand All @@ -274,14 +274,14 @@ func TestTokenSql(t *testing.T) {
OwnerType: "htlc",
IncludeDeleted: true,
},
expectedSql: "WHERE owner = true AND owner_type = $1 AND wallet_id = $2",
expectedArgs: []interface{}{"htlc", "me"},
expectedSql: "WHERE owner = true AND owner_type = $1 AND (wallet_id = $2 OR owner_wallet_id = $3)",
expectedArgs: []interface{}{"htlc", "me", "me"},
},
{
name: "owner and type",
params: driver.QueryTokenDetailsParams{TokenType: "tok", WalletID: "me"},
expectedSql: "WHERE owner = true AND wallet_id = $1 AND token_type = $2 AND is_deleted = false",
expectedArgs: []interface{}{"me", "tok"},
expectedSql: "WHERE owner = true AND (wallet_id = $1 OR owner_wallet_id = $2) AND token_type = $3 AND is_deleted = false",
expectedArgs: []interface{}{"me", "me", "tok"},
},
{
name: "owner and type and id",
Expand All @@ -290,8 +290,8 @@ func TestTokenSql(t *testing.T) {
WalletID: "me",
IDs: []*token.ID{{TxId: "a", Index: 1}},
},
expectedSql: "WHERE owner = true AND wallet_id = $1 AND token_type = $2 AND (tx_id, idx) IN ( ($3, $4) ) AND is_deleted = false",
expectedArgs: []interface{}{"me", "tok", "a", 1},
expectedSql: "WHERE owner = true AND (wallet_id = $1 OR owner_wallet_id = $2) AND token_type = $3 AND (tx_id, idx) IN ( ($4, $5) ) AND is_deleted = false",
expectedArgs: []interface{}{"me", "me", "tok", "a", 1},
},
{
name: "type and ids",
Expand All @@ -316,9 +316,9 @@ func TestTokenSql(t *testing.T) {
IDs: []*token.ID{{TxId: "a", Index: 1}},
WalletID: "me",
}, "A", "B")
assert.Equal(t, "WHERE owner = true AND wallet_id = $1 AND (A.tx_id, A.idx) IN ( ($2, $3) ) AND is_deleted = false", where, "join")
assert.Equal(t, "WHERE owner = true AND (wallet_id = $1 OR owner_wallet_id = $2) AND (A.tx_id, A.idx) IN ( ($3, $4) ) AND is_deleted = false", where, "join")
assert.Equal(t, "LEFT JOIN B ON A.tx_id = B.tx_id AND A.idx = B.idx", join, "join")
assert.Len(t, args, 3)
assert.Len(t, args, 4)
}

func TestTokenSqlNoJoin(t *testing.T) {
Expand Down
34 changes: 34 additions & 0 deletions token/services/db/sql/common/test_cases.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,34 @@ func TSaveAndGetToken(t *testing.T, db *TokenDB) {
assert.Len(t, unsp, 1)
assert.Equal(t, "0x02", unsp[0].Quantity)
assert.Equal(t, "ABC", unsp[0].Type)

tr = driver.TokenRecord{
TxID: fmt.Sprintf("tx%d", 2000),
Index: 0,
IssuerRaw: []byte{},
OwnerRaw: []byte{1, 2, 3},
OwnerType: "idemix",
OwnerIdentity: []byte{},
OwnerWalletID: "pineapple",
Ledger: []byte("ledger"),
LedgerMetadata: []byte{},
Quantity: "0x02",
Type: "ABC",
Amount: 2,
Owner: true,
Auditor: false,
Issuer: false,
}
assert.NoError(t, db.StoreToken(tr, nil))
_, err = db.GetTokens(&token.ID{TxId: fmt.Sprintf("tx%d", 2000), Index: 0})
assert.NoError(t, err)

tx, err := db.NewTokenDBTransaction(context.TODO())
assert.NoError(t, err)
_, owners, err := tx.GetToken(context.TODO(), fmt.Sprintf("tx%d", 2000), 0, true)
assert.NoError(t, err)
assert.Len(t, owners, 1)
assert.NoError(t, tx.Rollback())
}

func getTokensBy(t *testing.T, db *TokenDB, ownerEID, typ string) []*token.UnspentToken {
Expand Down Expand Up @@ -391,6 +419,7 @@ func TListAuditTokens(t *testing.T, db *TokenDB) {
OwnerRaw: []byte{1, 2},
OwnerType: "idemix",
OwnerIdentity: []byte{},
OwnerWalletID: "idemix",
Ledger: []byte("ledger"),
LedgerMetadata: []byte{},
Quantity: "0x01",
Expand All @@ -407,6 +436,7 @@ func TListAuditTokens(t *testing.T, db *TokenDB) {
OwnerRaw: []byte{3, 4},
OwnerType: "idemix",
OwnerIdentity: []byte{},
OwnerWalletID: "idemix",
Ledger: []byte("ledger"),
LedgerMetadata: []byte{},
Quantity: "0x02",
Expand All @@ -423,6 +453,7 @@ func TListAuditTokens(t *testing.T, db *TokenDB) {
OwnerRaw: []byte{5, 6},
OwnerType: "idemix",
OwnerIdentity: []byte{},
OwnerWalletID: "idemix",
Ledger: []byte("ledger"),
LedgerMetadata: []byte{},
Quantity: "0x03",
Expand Down Expand Up @@ -460,6 +491,7 @@ func TListIssuedTokens(t *testing.T, db *TokenDB) {
OwnerRaw: []byte{1, 2},
OwnerType: "idemix",
OwnerIdentity: []byte{},
OwnerWalletID: "idemix",
IssuerRaw: []byte{11, 12},
Ledger: []byte("ledger"),
LedgerMetadata: []byte{},
Expand All @@ -477,6 +509,7 @@ func TListIssuedTokens(t *testing.T, db *TokenDB) {
OwnerRaw: []byte{3, 4},
OwnerType: "idemix",
OwnerIdentity: []byte{},
OwnerWalletID: "idemix",
IssuerRaw: []byte{13, 14},
Ledger: []byte("ledger"),
LedgerMetadata: []byte{},
Expand All @@ -494,6 +527,7 @@ func TListIssuedTokens(t *testing.T, db *TokenDB) {
OwnerRaw: []byte{5, 6},
OwnerType: "idemix",
OwnerIdentity: []byte{},
OwnerWalletID: "idemix",
IssuerRaw: []byte{15, 16},
Ledger: []byte("ledger"),
LedgerMetadata: []byte{},
Expand Down
45 changes: 27 additions & 18 deletions token/services/db/sql/common/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ func (db *TokenDB) UnspentTokensIteratorBy(ctx context.Context, walletID, tokenT
return &UnspentTokensIterator{txs: rows}, err
}

// MinTokenInfoIteratorBy returns the minimum information about the tokens needed for the selector
func (db *TokenDB) MinTokenInfoIteratorBy(ctx context.Context, walletID string, typ string) (tdriver.MinTokenInfoIterator, error) {
// UnspentTokensInWalletIterator returns the minimum information about the tokens needed for the selector
func (db *TokenDB) SpendableTokensIteratorBy(ctx context.Context, walletID string, typ string) (tdriver.SpendableTokensIterator, error) {
span := trace.SpanFromContext(ctx)
where, args := tokenQuerySqlNoJoin(driver.QueryTokenDetailsParams{
WalletID: walletID,
Expand All @@ -155,7 +155,7 @@ func (db *TokenDB) MinTokenInfoIteratorBy(ctx context.Context, walletID string,
if err != nil {
return nil, errors.Wrapf(err, "error querying db")
}
return &MinTokenInfoIterator{txs: rows}, nil
return &UnspentTokensInWalletIterator{txs: rows}, nil
}

// Balance returns the sun of the amounts, with 64 bits of precision, of the tokens with type and EID equal to those passed as arguments.
Expand All @@ -182,9 +182,9 @@ func (db *TokenDB) Balance(walletID, typ string) (uint64, error) {
}

// ListUnspentTokensBy returns the list of unspent tokens, filtered by owner and token type
func (db *TokenDB) ListUnspentTokensBy(ownerEID, typ string) (*token.UnspentTokens, error) {
logger.Debugf("list unspent token by [%s,%s]", ownerEID, typ)
it, err := db.UnspentTokensIteratorBy(context.TODO(), ownerEID, typ)
func (db *TokenDB) ListUnspentTokensBy(walletID, typ string) (*token.UnspentTokens, error) {
logger.Debugf("list unspent token by [%s,%s]", walletID, typ)
it, err := db.UnspentTokensIteratorBy(context.TODO(), walletID, typ)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -855,7 +855,7 @@ func (t *TokenTransaction) GetToken(ctx context.Context, txID string, index uint
IncludeDeleted: includeDeleted,
}, t.db.table.Tokens, t.db.table.Ownership)

query := fmt.Sprintf("SELECT owner_raw, token_type, quantity, wallet_id FROM %s %s %s", t.db.table.Tokens, join, where)
query := fmt.Sprintf("SELECT owner_raw, token_type, quantity, %s.wallet_id, owner_wallet_id FROM %s %s %s", t.db.table.Ownership, t.db.table.Tokens, join, where)
span.AddEvent("query", tracing.WithAttributes(tracing.String(QueryLabel, query)))
logger.Debug(query, args)
rows, err := t.tx.Query(query, args...)
Expand All @@ -869,18 +869,26 @@ func (t *TokenTransaction) GetToken(ctx context.Context, txID string, index uint
var tokenType string
var quantity string
owners := []string{}
var walletID *string
for rows.Next() {
var owner string
if err := rows.Scan(&raw, &tokenType, &quantity, &owner); err != nil {
var tempOwner *string
if err := rows.Scan(&raw, &tokenType, &quantity, &tempOwner, &walletID); err != nil {
return nil, owners, err
}
var owner string
if tempOwner != nil {
owner = *tempOwner
}
if len(owner) > 0 {
owners = append(owners, owner)
}
}
if rows.Err() != nil {
return nil, nil, rows.Err()
}
if walletID != nil && len(*walletID) != 0 {
owners = append(owners, *walletID)
}
span.AddEvent("end_scan_rows", tracing.WithAttributes(tracing.Int(ResultRowsLabel, len(owners))))
if len(raw) == 0 {
return nil, owners, nil
Expand Down Expand Up @@ -911,6 +919,10 @@ func (t *TokenTransaction) Delete(ctx context.Context, txID string, index uint64
}

func (t *TokenTransaction) StoreToken(ctx context.Context, tr driver.TokenRecord, owners []string) error {
if len(tr.OwnerWalletID) == 0 && len(owners) == 0 && tr.Owner {
return errors.Errorf("no owners specified [%s]", string(debug.Stack()))
}

span := trace.SpanFromContext(ctx)
//logger.Debugf("store record [%s:%d,%v] in table [%s]", tr.TxID, tr.Index, owners, t.db.table.Tokens)

Expand Down Expand Up @@ -958,9 +970,6 @@ func (t *TokenTransaction) StoreToken(ctx context.Context, tr driver.TokenRecord

// Store ownership
span.AddEvent("store_ownerships")
if len(tr.OwnerWalletID) != 0 {
owners = append(owners, tr.OwnerWalletID)
}
for _, eid := range owners {
query = fmt.Sprintf("INSERT INTO %s (tx_id, idx, wallet_id) VALUES ($1, $2, $3)", t.db.table.Ownership)
logger.Debug(query, tr.TxID, tr.Index, eid)
Expand All @@ -981,26 +990,26 @@ func (t *TokenTransaction) Rollback() error {
return t.tx.Rollback()
}

type MinTokenInfoIterator struct {
type UnspentTokensInWalletIterator struct {
txs *sql.Rows
}

func (u *MinTokenInfoIterator) Close() {
func (u *UnspentTokensInWalletIterator) Close() {
u.txs.Close()
}

func (u *MinTokenInfoIterator) Next() (*token.MinTokenInfo, error) {
func (u *UnspentTokensInWalletIterator) Next() (*token.UnspentTokenInWallet, error) {
if !u.txs.Next() {
return nil, nil
}

tok := &token.MinTokenInfo{
tok := &token.UnspentTokenInWallet{
Id: &token.ID{},
Owner: "",
WalletID: "",
Type: "",
Quantity: "",
}
if err := u.txs.Scan(&tok.Id.TxId, &tok.Id.Index, &tok.Type, &tok.Quantity, &tok.Owner); err != nil {
if err := u.txs.Scan(&tok.Id.TxId, &tok.Id.Index, &tok.Type, &tok.Quantity, &tok.WalletID); err != nil {
return nil, err
}
return tok, nil
Expand Down
Loading

0 comments on commit 83744d4

Please sign in to comment.