common

package
v0.27.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 43 Imported by: 0

Documentation

Overview

Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0

Index

Constants

View Source
const (
	// ConfigKeyTableNames is the absolute configuration key for global SQL table name overrides.
	//
	// This key is resolved against the root configuration provider (not a TMS-scoped
	// configuration), so it applies to all TMS instances on the node and maps directly
	// to token.storage.tableNames in the configuration file.
	//
	// Example YAML:
	//
	//   token:
	//     storage:
	//       tableNames:
	//         id_signers: identity_signers
	//         tokens: my_tokens
	ConfigKeyTableNames = "token.storage.tableNames"

	// ConfigKeySkipPrefix is the absolute configuration key for disabling the
	// FSC-generated prefix on SQL table names.
	//
	// When set to true, table names are produced without any prefix, regardless
	// of what prefix is supplied at runtime. Default is false.
	//
	// Example YAML:
	//
	//   token:
	//     storage:
	//       skipPrefix: true
	ConfigKeySkipPrefix = "token.storage.skipPrefix"

	// ConfigKeyLockStrategy is the absolute configuration key for the
	// token-lock acquisition strategy used by SQL backends that support more
	// than one (currently Postgres only; other backends read and ignore it).
	//
	// Example YAML:
	//
	//   token:
	//     storage:
	//       db:
	//         lockStrategy: skipLocked
	ConfigKeyLockStrategy = "token.storage.db.lockStrategy"

	// LockStrategyInsert is the default lock strategy: INSERT and catch the
	// unique-constraint violation on a lost race. Equivalent to leaving
	// ConfigKeyLockStrategy unset.
	LockStrategyInsert = "insert"
	// LockStrategyOnConflict acquires a lock with
	// INSERT ... ON CONFLICT DO NOTHING RETURNING, avoiding a server-side
	// unique-violation error on a lost race.
	LockStrategyOnConflict = "onConflict"
	// LockStrategySkipLocked behaves like LockStrategyOnConflict for a single
	// lock, and additionally allows a covering-window batch claim using
	// FOR UPDATE SKIP LOCKED to avoid colliding with rows a concurrent
	// claimant is already processing.
	LockStrategySkipLocked = "skipLocked"

	// ConfigKeyPoolMetricsInterval is the absolute configuration key for how
	// often the Postgres connection-pool sampler reads (*sql.DB).Stats() and
	// updates db_pool_in_use/db_pool_max_open/db_pool_wait_*. Unlike its
	// siblings above, this key is observability only: it changes nothing about
	// how storage operates, only how often it is reported on. Default is 1s;
	// 0 disables the sampler (and its goroutine) entirely, for an operator who
	// does not scrape Prometheus.
	//
	// Example YAML:
	//
	//   token:
	//     storage:
	//       db:
	//         poolMetricsInterval: 1s
	ConfigKeyPoolMetricsInterval = "token.storage.db.poolMetricsInterval"

	// DefaultPoolMetricsInterval is used when ConfigKeyPoolMetricsInterval is
	// unset. 1s rather than a coarser interval because the signal it exists to
	// catch - a pool held near-saturated by connections with a short average
	// hold time, such as the ~250ms settlement hold in #2474 - would be missed
	// most of the time by a slower poll: at 10s the duty cycle is roughly
	// 1-in-40, at 1s roughly 1-in-4, and a *sustained* exhaustion episode (the
	// kind that actually hangs Select calls) is sampled reliably either way.
	// Burst detection does not depend on this interval: db_pool_wait_count_total
	// increments once per blocked checkout regardless of how often it is
	// scraped, so the gauge's job is occupancy context, not detection.
	DefaultPoolMetricsInterval = time.Second
)

Variables

This section is empty.

Functions

func Close

func Close(closer Closer)

func CloseFunc

func CloseFunc(closer func() error)

func CloseRWDB added in v0.24.0

func CloseRWDB(readDB *sql.DB, writeDB WriteDB) error

CloseRWDB closes a store's read and write handles, joining their errors.

It replaces FSC's Close, which takes two *sql.DB and compares them to avoid closing the same pool twice. That comparison cannot survive a decorated write handle, and it is not needed: sql.DB.Close is idempotent, so closing one pool through both handles is harmless.

A handle that is nil is skipped rather than dereferenced, so closing a store that failed part-way through construction reports the errors it has instead of panicking.

func HasMovementsParams

func HasMovementsParams(params driver2.QueryMovementsParams) cond.Condition

func HasTokenDetails

func HasTokenDetails(params driver2.QueryTokenDetailsParams, tokenTable common.Table) cond.Condition

func HasTokens

func HasTokens(colTxID, colIdx common.FieldName, ids ...*token.ID) cond.Condition

func HasTransactionParams

func HasTransactionParams(params driver2.QueryTransactionsParams, table common.Table) cond.Condition

func HasValidationParams

func HasValidationParams(params driver2.QueryValidationRecordsParams, tableName string) cond.Condition

func IsExpiredToken

func IsExpiredToken(tokenRequests, tokenLocks common3.Table, leaseExpiry time.Duration) cond.Condition

func IsStaleLock added in v0.18.0

func IsStaleLock(tokenRequests, tokenLocks common3.Table, leaseExpiry time.Duration) cond.Condition

IsStaleLock matches the lock rows whose lease has aged out, or whose consuming transaction is Deleted or Orphan. The correlation is on consumer_tx_id, the transaction that is trying to spend the token: (tx_id, idx) identifies the locked token, i.e. the transaction that created it, whose status says nothing about whether the lock is still live. The condition is correlated rather than a partial-key IN on tx_id, so cleanup removes only the matching (tx_id, idx) rows and leaves the other indices of the same transaction locked. See #2018.

The argument order matches IsExpiredToken (tokenRequests first, tokenLocks second) so a swapped call is immediately visible and consistent across this file.

func RunBalancePreparedComparison added in v0.15.1

func RunBalancePreparedComparison(b *testing.B, store *TokenStore)

RunBalancePreparedComparison benchmarks balance against a version of the same query executed via a statement prepared once outside the timed loop, using the same seed data, worker count, and duration as RunTokenStoreBenchmarks so the numbers are directly comparable.

This exists to answer: "how much would moving to prepared statements help for wallet balance checks?" balance() has a single caller (Balance) that always sets only WalletID/TokenType, matching the same shape as UnspentTokensIteratorBy/SpendableTokensIteratorBy.

func RunGetStatusPreparedComparison added in v0.16.0

func RunGetStatusPreparedComparison(b *testing.B, store *TransactionStore)

func RunGetTokenRequestPreparedComparison added in v0.16.0

func RunGetTokenRequestPreparedComparison(b *testing.B, store *TransactionStore)

RunGetStatusPreparedComparison benchmarks GetStatus against a version of the same query executed via a statement prepared once outside the timed loop, using the same worker count and duration as RunTokenStoreBenchmarks so the numbers are directly comparable.

Unlike the token-store queries, GetStatus has exactly one query shape (tx_id = ?, always present) - this benchmark exists to confirm the win holds for that simpler, single-shape case, backing the finality-polling loop in ttx/finality.go. RunGetTokenRequestPreparedComparison benchmarks GetTokenRequest against a version of the same query executed via a statement prepared once outside the timed loop. Same single-shape reasoning as GetStatus.

func RunSpendableTokensIteratorByPreparedComparison added in v0.15.1

func RunSpendableTokensIteratorByPreparedComparison(b *testing.B, store *TokenStore)

RunSpendableTokensIteratorByPreparedComparison benchmarks SpendableTokensIteratorBy against a version of the same query executed via a statement prepared once outside the timed loop, using the same seed data, worker count, and duration as RunTokenStoreBenchmarks so the numbers are directly comparable.

This exists to answer: "how much would moving to prepared statements help for the selector's spend-fetch query?" (see #1919). The query built by SpendableTokensIteratorBy is dynamic (its shape depends on whether walletID / typ are empty), so this comparison fixes both parameters to non-empty values matching the seeded data, which is the common case in production (see token/services/selector/sherdlock/fetcher.go).

func RunTokenStoreBenchmarks

func RunTokenStoreBenchmarks(b *testing.B, store *TokenStore)

func RunUnspentTokensIteratorByPreparedComparison added in v0.14.2

func RunUnspentTokensIteratorByPreparedComparison(b *testing.B, store *TokenStore)

RunUnspentTokensIteratorByPreparedComparison benchmarks UnspentTokensIteratorBy against a version of the same query executed via a statement prepared once outside the timed loop, using the same seed data, worker count, and duration as RunTokenStoreBenchmarks so the numbers are directly comparable.

This exists to answer: "how much would moving to prepared statements help for our primary token-selection query?" (see #1183). The query built by UnspentTokensIteratorBy is dynamic (its shape depends on whether walletID / tokenType are empty), so this comparison fixes both parameters to non-empty values matching the seeded data, which is the common case in production.

func SeedBenchTokens

func SeedBenchTokens(b *testing.B, store *TokenStore, n int)

func TestAWAddMovement

func TestAWAddMovement(t *testing.T, store transactionsStoreConstructor)

func TestAWAddMovementContextCancelled added in v0.15.1

func TestAWAddMovementContextCancelled(t *testing.T, store transactionsStoreConstructor)

TestAWAddMovementContextCancelled verifies that a cancelled context is propagated from ExecContext through AddMovement inside a TransactionStoreTransaction.

func TestAWAddTokenRequest

func TestAWAddTokenRequest(t *testing.T, store transactionsStoreConstructor)

func TestAWAddTokenRequestContextCancelled added in v0.15.1

func TestAWAddTokenRequestContextCancelled(t *testing.T, store transactionsStoreConstructor)

TestAWAddTokenRequestContextCancelled verifies that a cancelled context is propagated from ExecContext through AddTokenRequest inside a TransactionStoreTransaction.

func TestAWAddTransaction

func TestAWAddTransaction(t *testing.T, store transactionsStoreConstructor)

func TestAWAddTransactionContextCancelled added in v0.15.1

func TestAWAddTransactionContextCancelled(t *testing.T, store transactionsStoreConstructor)

TestAWAddTransactionContextCancelled verifies that a cancelled context is propagated from ExecContext through AddTransaction inside a TransactionStoreTransaction.

func TestAWAddValidationRecord

func TestAWAddValidationRecord(t *testing.T, store endorserStoreConstructor)

func TestAddTransactionEndorsementAck

func TestAddTransactionEndorsementAck(t *testing.T, store transactionsStoreConstructor)

func TestAddTransactionEndorsementAckContextCancelled added in v0.15.1

func TestAddTransactionEndorsementAckContextCancelled(t *testing.T, store transactionsStoreConstructor)

TestAddTransactionEndorsementAckContextCancelled verifies that a cancelled context is propagated from ExecContext back to the caller of AddTransactionEndorsementAck.

func TestCountOpenFindings added in v0.24.0

func TestCountOpenFindings(t *testing.T, store transactionsStoreConstructor)

TestCountOpenFindings verifies the aggregate query groups open findings by severity and excludes resolved ones, and that the rows scan into the returned map keyed by the raw severity value.

func TestCountOpenFindings_NoOpenFindings added in v0.24.0

func TestCountOpenFindings_NoOpenFindings(t *testing.T, store transactionsStoreConstructor)

TestCountOpenFindings_NoOpenFindings verifies an empty result set (nothing open) returns an empty, non-nil map rather than an error.

func TestDeleteTokensContextCancelled added in v0.15.1

func TestDeleteTokensContextCancelled(t *testing.T, store tokenStoreConstructor)

TestDeleteTokensContextCancelled verifies that a cancelled context is propagated from ExecContext back to the caller of DeleteTokens as a context error.

func TestGetStatus

func TestGetStatus(t *testing.T, store transactionsStoreConstructor)

func TestGetStatusContextCancelled added in v0.15.1

func TestGetStatusContextCancelled(t *testing.T, store transactionsStoreConstructor)

TestGetStatusContextCancelled verifies that a cancelled context is propagated from QueryContext back to the caller of GetStatus as a context error.

func TestGetStatusEndorser

func TestGetStatusEndorser(t *testing.T, store endorserStoreConstructor)

func TestGetTokenRequest

func TestGetTokenRequest(t *testing.T, store transactionsStoreConstructor)

func TestGetTransactionEndorsementAcks

func TestGetTransactionEndorsementAcks(t *testing.T, store transactionsStoreConstructor)

func TestGetWalletID

func TestGetWalletID(t *testing.T, store walletStoreConstructor)

func TestGetWalletIDNotFound added in v0.21.0

func TestGetWalletIDNotFound(t *testing.T, store walletStoreConstructor)

TestGetWalletIDNotFound pins the not-found contract of the WalletStoreService: an identity with no stored binding resolves to ("", nil), never an error. The role Registry relies on this to tell a transient storage failure apart from "this identity was never registered" (issue #2063).

func TestGetWalletIDs

func TestGetWalletIDs(t *testing.T, store walletStoreConstructor)

func TestIdentityExists

func TestIdentityExists(t *testing.T, store walletStoreConstructor)

func TestIdentityExistsPropagatesQueryError added in v0.24.0

func TestIdentityExistsPropagatesQueryError(t *testing.T, store walletStoreConstructor)

TestIdentityExistsPropagatesQueryError asserts that a failed lookup surfaces as an error rather than as a negative answer.

func TestLoadMeta

func TestLoadMeta(t *testing.T, store walletStoreConstructor)

func TestLock

func TestLock(t *testing.T, store tokenLockStoreConstructor)

func TestLockContextCancelled added in v0.15.1

func TestLockContextCancelled(t *testing.T, store tokenLockStoreConstructor)

TestLockContextCancelled verifies that Lock propagates context cancellation from ExecContext back to the caller as a context error.

func TestQueryMovements

func TestQueryMovements(t *testing.T, store transactionsStoreConstructor)

func TestQueryTokenRequests

func TestQueryTokenRequests(t *testing.T, store transactionsStoreConstructor)

func TestQueryTransactions

func TestQueryTransactions(t *testing.T, store transactionsStoreConstructor)

func TestQueryValidations

func TestQueryValidations(t *testing.T, store endorserStoreConstructor)

func TestSetStatus

func TestSetStatus(t *testing.T, store transactionsStoreConstructor)

func TestSetStatusContextCancelled added in v0.15.1

func TestSetStatusContextCancelled(t *testing.T, store transactionsStoreConstructor)

TestSetStatusContextCancelled verifies that a cancelled context is propagated from ExecContext back to the caller of SetStatus as a context error.

func TestSetStatusEndorser

func TestSetStatusEndorser(t *testing.T, store endorserStoreConstructor)

func TestStoreIdentity

func TestStoreIdentity(t *testing.T, store walletStoreConstructor)

func TestStoreIdentityIdempotent

func TestStoreIdentityIdempotent(t *testing.T, store walletStoreConstructor)

func TestUnlockByTxID

func TestUnlockByTxID(t *testing.T, store tokenLockStoreConstructor)

func TestUnlockByTxIDContextCancelled added in v0.15.1

func TestUnlockByTxIDContextCancelled(t *testing.T, store tokenLockStoreConstructor)

TestUnlockByTxIDContextCancelled verifies that UnlockByTxID propagates context cancellation from ExecContext back to the caller as a context error.

Types

type AnyUUID

type AnyUUID struct{}

func (AnyUUID) Match

func (a AnyUUID) Match(v driver2.Value) bool

type BigInt

type BigInt struct {
	*big.Int
}

BigInt is a custom wrapper around math/big.Int that implements sql.Scanner for reading NUMERIC values from the DB

func (*BigInt) Scan

func (b *BigInt) Scan(value any) error

Scan implements the sql.Scanner interface for reading from the DB

type Closer

type Closer interface {
	Close() error
}

type EndorserStore

type EndorserStore struct {
	// contains filtered or unexported fields
}

EndorserStore manages validation records for token transaction endorsements

func NewEndorserStore

func NewEndorserStore(
	readDB *sql.DB,
	writeDB WriteDB,
	tables TableNames,
	ci common3.CondInterpreter,
	pi common3.PagInterpreter,
) (*EndorserStore, error)

NewEndorserStore creates a new EndorserStore

func (*EndorserStore) Close

func (db *EndorserStore) Close() error

Close closes the database connections

func (*EndorserStore) CreateSchema

func (db *EndorserStore) CreateSchema() error

CreateSchema creates the database schema for the endorser store

func (*EndorserStore) GetSchema

func (db *EndorserStore) GetSchema() string

GetSchema returns the SQL schema for creating the endorser store tables

func (*EndorserStore) GetStatus

func (db *EndorserStore) GetStatus(ctx context.Context, txID string) (dbdriver.TxStatus, string, error)

GetStatus returns the status of a validation record

func (*EndorserStore) NewEndorserStoreTransaction

func (db *EndorserStore) NewEndorserStoreTransaction() (dbdriver.EndorserStoreTransaction, error)

NewEndorserStoreTransaction creates a new transaction for endorser operations

func (*EndorserStore) QueryValidations

QueryValidations returns an iterator over validation records matching the given params

type EndorserStoreTransaction

type EndorserStoreTransaction struct {
	// contains filtered or unexported fields
}

EndorserStoreTransaction represents a database transaction for endorser operations

func (*EndorserStoreTransaction) AddValidationRecord

func (w *EndorserStoreTransaction) AddValidationRecord(ctx context.Context, txID string, tokenRequest []byte, meta map[string][]byte, ppHash driver2.PPHash) error

AddValidationRecord adds a validation record to the database.

Verification: as for TransactionStoreTransaction.AddTokenRequest, this layer does not interpret tokenRequest — the wire-format check lives in the endorserdb service. What is refused here is a row no later check could act on: an empty transaction id, empty request bytes, or an empty public parameters hash.

func (*EndorserStoreTransaction) Commit

func (w *EndorserStoreTransaction) Commit() error

Commit commits the transaction

func (*EndorserStoreTransaction) Impl

Impl returns the underlying transaction implementation

func (*EndorserStoreTransaction) Rollback

func (w *EndorserStoreTransaction) Rollback()

Rollback rolls back the transaction. A rollback failure cannot be returned (the interface returns nothing) but is logged rather than dropped, so a transaction that outlived its rollback leaves a trace. sql.ErrTxDone is expected when the transaction has already been committed or rolled back.

func (*EndorserStoreTransaction) SetStatus

func (w *EndorserStoreTransaction) SetStatus(ctx context.Context, txID string, status dbdriver.TxStatus, message string) error

SetStatus sets the status of a validation record

type IdentityStore

type IdentityStore struct {
	// contains filtered or unexported fields
}

IdentityStore is a SQL-backed implementation of the IdentityStore interface.

func NewCachedIdentityStore

func NewCachedIdentityStore(
	readDB *sql.DB,
	writeDB WriteDB,
	tables TableNames,
	ci common3.CondInterpreter,
	errorWrapper driver2.SQLErrorWrapper,
) (*IdentityStore, error)

func NewIdentityStore

func NewIdentityStore(
	readDB *sql.DB,
	writeDB WriteDB,
	tables TableNames,
	signerInfoCache cache[bool],
	auditInfoCache cache[[]byte],
	ci common3.CondInterpreter,
	errorWrapper driver2.SQLErrorWrapper,
) (*IdentityStore, error)

func NewIdentityStoreWithNotifier

func NewIdentityStoreWithNotifier(
	readDB *sql.DB,
	writeDB WriteDB,
	tables TableNames,
	signerInfoCache cache[bool],
	auditInfoCache cache[[]byte],
	ci common3.CondInterpreter,
	errorWrapper driver2.SQLErrorWrapper,
	notifier idriver.IdentityConfigurationNotifier,
) (*IdentityStore, error)

NewIdentityStoreWithNotifier creates a new IdentityStore with a notifier.

func NewNoCacheIdentityStore

func NewNoCacheIdentityStore(
	readDB *sql.DB,
	writeDB WriteDB,
	tables TableNames,
	ci common3.CondInterpreter,
	errorWrapper driver2.SQLErrorWrapper,
) (*IdentityStore, error)

func (*IdentityStore) AddConfiguration

func (db *IdentityStore) AddConfiguration(ctx context.Context, wp driver.IdentityConfiguration) error

AddConfiguration stores an identity configuration in the database. It also enqueues an event to the notifier if available.

func (*IdentityStore) Close

func (db *IdentityStore) Close() error

func (*IdentityStore) ConfigurationExists

func (db *IdentityStore) ConfigurationExists(ctx context.Context, id, typ, url string) (bool, error)

func (*IdentityStore) ConfigurationsByID added in v0.14.2

func (db *IdentityStore) ConfigurationsByID(ctx context.Context, id, configurationType string) ([]driver.IdentityConfiguration, error)

ConfigurationsByID returns all configurations with the given id and type, regardless of their url.

func (*IdentityStore) CreateSchema

func (db *IdentityStore) CreateSchema() error

func (*IdentityStore) GetAuditInfo

func (db *IdentityStore) GetAuditInfo(ctx context.Context, id []byte) ([]byte, error)

GetAuditInfo returns the audit info stored for id, or nil if none is stored.

Verification: the row is addressed by identity hash, so the identity stored alongside the audit info is compared against id before the audit info is returned or cached. Audit info is what an auditor uses to attribute a transaction to a party, so handing back audit info belonging to a different identity than the caller asked for would misattribute it. A row whose identity and identity_hash columns disagree is reported rather than returned.

func (*IdentityStore) GetConfiguration

func (db *IdentityStore) GetConfiguration(ctx context.Context, id, typ, url string) (*driver.IdentityConfiguration, error)

GetConfiguration returns the configuration with the given id, type, and url.

func (*IdentityStore) GetConfigurationID added in v0.18.0

func (db *IdentityStore) GetConfigurationID(ctx context.Context, id, typ, url string) (string, error)

GetConfigurationID returns the conf_id persisted for the configuration with the given id, type, and url. It returns the empty string if that configuration is not stored yet.

The column is read rather than recomputed from driver.IdentityConfiguration.UniqueID on purpose: conf_id is referenced by wallets.conf_id through a foreign key, so for a configuration stored by an earlier release the value on disk is the only one the constraint accepts, even when a newer encoding derives a different UniqueID for the same tuple.

func (*IdentityStore) GetExistingSignerInfo

func (db *IdentityStore) GetExistingSignerInfo(ctx context.Context, ids ...tdriver.Identity) ([]string, error)

GetExistingSignerInfo returns the unique ids of the passed identities for which signer info is stored.

Verification: an empty identity is excluded from the lookup rather than looked up, for the reason given on StoreSignerInfo — its unique id is the shared "<empty>" row key, so consulting it would report a row written for one caller as the signer info of any other empty identity, and identity.Provider.AreMe would answer that the empty identity is mine. It is excluded rather than rejected because this is an existence query over a batch: an empty entry is simply not an identity signer info can exist for, and failing the batch would also lose the answer for the caller's other identities.

func (*IdentityStore) GetSchema

func (db *IdentityStore) GetSchema() string

func (*IdentityStore) GetSignerInfo

func (db *IdentityStore) GetSignerInfo(ctx context.Context, identity []byte) ([]byte, error)

GetSignerInfo returns the signer info stored for identity, or nil if none is stored.

Verification: the row is addressed by identity hash, so the identity stored alongside the signer info is compared against the requested one before the info is returned. Signer info is what a key manager resolves into a signer, so returning the info of a different identity than the caller named would route signing to the wrong key. A row whose identity and identity_hash columns disagree is reported rather than returned.

func (*IdentityStore) GetTokenInfo

func (db *IdentityStore) GetTokenInfo(ctx context.Context, id []byte) ([]byte, []byte, error)

GetTokenInfo returns the token metadata stored for id, or nil if none is stored.

Verification: as for GetAuditInfo — an empty id is refused, and the identity stored alongside the metadata is compared against id before the metadata is returned. Token metadata is what the owner of a token uses to recognise and spend it, so metadata belonging to a different identity is not a usable substitute for the caller's own.

func (*IdentityStore) IterateSigners

func (db *IdentityStore) IterateSigners(ctx context.Context, offset, limit int) ([]idriver.SignerEntry, error)

IterateSigners returns a page of SignerEntry values from the Signers table, ordered by identity_hash, starting at the given offset and returning at most limit entries.

func (*IdentityStore) IteratorConfigurations

func (db *IdentityStore) IteratorConfigurations(ctx context.Context, configurationType string) (idriver.IdentityConfigurationIterator, error)

IteratorConfigurations returns an iterator to all configurations of the given type.

func (*IdentityStore) Notifier

Notifier returns the IdentityNotifier associated with this store.

func (*IdentityStore) RegisterIdentityDescriptor

func (db *IdentityStore) RegisterIdentityDescriptor(ctx context.Context, descriptor *idriver.IdentityDescriptor, alias tdriver.Identity) error

RegisterIdentityDescriptor stores the signer info and audit info of the descriptor's identity and of the given alias, and refreshes the corresponding caches. All writes happen in a single transaction. The operation is idempotent and safe to retry: rows that are already present are left untouched and the missing ones are written, so both a full re-registration and the retry of a registration that was only partially persisted succeed.

func (*IdentityStore) SignerInfoExists

func (db *IdentityStore) SignerInfoExists(ctx context.Context, id []byte) (bool, error)

SignerInfoExists returns true if signer info is stored for id.

Verification: as for GetExistingSignerInfo, which this delegates to — an empty id is never looked up, so it reads as "no signer info" and the shared "<empty>" row key stays unconsulted.

func (*IdentityStore) StoreIdentityData

func (db *IdentityStore) StoreIdentityData(ctx context.Context, id []byte, identityAudit []byte, tokenMetadata []byte, tokenMetadataAudit []byte) error

StoreIdentityData binds id to its audit info and token metadata.

Verification: an empty id is refused. Rows are keyed by tdriver.Identity.UniqueID, which maps the empty identity to the constant "<empty>" rather than to a hash, so an empty identity would write to a well-known key that any later empty-identity lookup reads back as its own.

func (*IdentityStore) StoreSignerInfo

func (db *IdentityStore) StoreSignerInfo(ctx context.Context, id tdriver.Identity, info []byte) error

StoreSignerInfo binds id to the signer info a key manager resolves into a signer.

Verification: an empty id is refused, for the reason given on StoreIdentityData — the empty identity does not hash, it maps to the shared "<empty>" row key.

type KeystoreStore

type KeystoreStore struct {
	// contains filtered or unexported fields
}

func NewKeystoreStore

func NewKeystoreStore(readDB *sql.DB, writeDB WriteDB, tables TableNames, ci qcommon.CondInterpreter, errorWrapper driver.SQLErrorWrapper) (*KeystoreStore, error)

func (*KeystoreStore) Close

func (db *KeystoreStore) Close() error

func (*KeystoreStore) CreateSchema

func (db *KeystoreStore) CreateSchema() error

func (*KeystoreStore) Delete

func (db *KeystoreStore) Delete(key string) error

func (*KeystoreStore) Get

func (db *KeystoreStore) Get(key string, state any) error

func (*KeystoreStore) GetRaw

func (db *KeystoreStore) GetRaw(key string) ([]byte, error)

func (*KeystoreStore) GetSchema

func (db *KeystoreStore) GetSchema() string

func (*KeystoreStore) Put

func (db *KeystoreStore) Put(key string, state any) error

type PersistenceConstructor

type PersistenceConstructor[V common.DBObject] func(*common.RWDB, TableNames) (V, error)

type PreparedStmtHolder added in v0.15.1

type PreparedStmtHolder[K comparable] interface {
	// Execute prepares (or reuses) the statement cached under key and runs
	// it. buildQuery is called on every invocation (hit or miss) since it
	// captures the caller's real argument values via closure; only its
	// returned SQL text is discarded on a cache hit, the args are always
	// used. If preparing or running the cached statement fails, Execute
	// falls back to running the query directly (unprepared, uncached).
	// Callers are responsible for closing the returned rows.
	Execute(ctx context.Context, db *sql.DB, key K, buildQuery func() (string, []any, error)) (*sql.Rows, error)

	// Count returns the number of cached prepared statements. Intended for
	// tests verifying statement reuse across argument shapes.
	Count() int

	// Close closes all cached prepared statements.
	Close() error
}

PreparedStmtHolder caches prepared statements keyed by K, executing a query built by the caller and falling back to an unprepared query if preparing or executing the cached statement fails. It centralizes the get-or-prepare-and-execute logic shared by any query that runs repeatedly with the same SQL shape but different bound argument values (see #1183). TokenStore instantiates one holder per query that benefits from this treatment. Exported as an interface so stores can inject a fake for unit testing.

type StorageConfig

type StorageConfig struct {
	// TableNames contains optional short-code overrides for individual table names.
	TableNames TableNamesConfig
	// SkipPrefix disables the FSC-generated prefix on all table names when true.
	// Default is false.
	SkipPrefix bool
	// LockStrategy selects the token-lock acquisition strategy. One of
	// LockStrategyInsert (default), LockStrategyOnConflict, or
	// LockStrategySkipLocked. Only acted upon by the Postgres driver.
	LockStrategy string
	// PoolMetricsInterval is how often the connection-pool sampler reads
	// (*sql.DB).Stats(). Defaults to DefaultPoolMetricsInterval; 0 disables
	// the sampler. Only acted upon by the Postgres driver.
	PoolMetricsInterval time.Duration
}

StorageConfig holds all SQL storage configuration options that can be read from the configuration provider.

func LoadStorageConfig

func LoadStorageConfig(cfg driver2.Config) (StorageConfig, error)

LoadStorageConfig reads all SQL storage options from cfg. It returns a zero-value StorageConfig (and nil error) when cfg is nil.

On a validation error for one option, it still returns every option parsed successfully so far (with LockStrategy defaulted to LockStrategyInsert), rather than discarding them. Callers that warn-and-continue on error (e.g. postgres/driver.go, sqlite/driver.go) otherwise silently lose TableNames/SkipPrefix to an unrelated typo in lockStrategy - see #2395 Phase 7.

type TableNameCreator

type TableNameCreator struct {
	// contains filtered or unexported fields
}

func NewTableNameCreator

func NewTableNameCreator(defaultPrefix string) *TableNameCreator

func (*TableNameCreator) CreateTableName

func (c *TableNameCreator) CreateTableName(tablePrefix, name string, params ...string) (string, error)

func (*TableNameCreator) GetFormatter

func (c *TableNameCreator) GetFormatter(prefix string) (*tableNameFormatter, error)

func (*TableNameCreator) MustGetTableName

func (c *TableNameCreator) MustGetTableName(tablePrefix, name string, params ...string) string

type TableNames

type TableNames struct {
	Prefix                 string
	Params                 []string
	Movements              string
	Transactions           string
	Requests               string
	Validations            string
	TransactionEndorseAck  string
	Certifications         string
	Tokens                 string
	Ownership              string
	PublicParams           string
	Wallets                string
	IdentityConfigurations string
	IdentityInfo           string
	Signers                string
	TokenLocks             string
	KeyStore               string
	EIDLeases              string
	TokenSKICleanups       string
	Findings               string
}

func GetTableNames

func GetTableNames(prefix string, params ...string) (TableNames, error)

GetTableNames returns the SQL table names for the given prefix and params using the default short codes. It is equivalent to calling GetTableNamesWithOverrides with a nil overrides map.

func GetTableNamesWithConfig

func GetTableNamesWithConfig(prefix string, cfg StorageConfig, params ...string) (TableNames, error)

GetTableNamesWithConfig returns the SQL table names using all options from cfg.

func GetTableNamesWithOverrides

func GetTableNamesWithOverrides(prefix string, overrides TableNamesConfig, params ...string) (TableNames, error)

GetTableNamesWithOverrides returns the SQL table names for the given prefix and params, applying any short-code substitutions from overrides before the FSC formatter wraps them with the prefix and params.

The overrides map key is the canonical short code (e.g. "id_signers") and the value is the replacement short code (e.g. "identity_signers"). Unknown keys produce a warning and are ignored; all other fields keep their default values.

func GetTableNamesWithOverridesSkipPrefix

func GetTableNamesWithOverridesSkipPrefix(prefix string, overrides TableNamesConfig, params ...string) (TableNames, error)

GetTableNamesWithOverridesSkipPrefix is like GetTableNamesWithOverrides but omits the FSC-generated prefix from every table name.

type TableNamesConfig

type TableNamesConfig map[string]string

TableNamesConfig maps a short code (e.g. "id_signers") to the replacement short code that will be used when generating the final SQL table name. The FSC-generated prefix and params are still applied around the replacement value.

Unknown keys are warned and ignored. Omitted keys keep their default short codes.

func LoadTableNamesConfig

func LoadTableNamesConfig(cfg driver2.Config) (TableNamesConfig, error)

LoadTableNamesConfig reads the table name overrides from cfg. It returns an empty map (and nil error) when cfg is nil or the config key is absent.

type TokenLockStore

type TokenLockStore struct {
	ReadDB  *sql.DB
	WriteDB WriteDB
	Table   tokenLockTables
	Logger  logging.Logger
	// contains filtered or unexported fields
}

func NewTokenLockStore

func NewTokenLockStore(readDB *sql.DB, writeDB WriteDB, tables TableNames, ci common3.CondInterpreter, errorWrapper fscdriver.SQLErrorWrapper) (*TokenLockStore, error)

func (*TokenLockStore) Cleanup added in v0.18.0

func (db *TokenLockStore) Cleanup(ctx context.Context, leaseExpiry time.Duration) error

Cleanup releases the stale token locks: those whose consuming transaction is Deleted or Orphan, and those whose lease is older than leaseExpiry. Only the affected (tx_id, idx) rows are deleted. The same statement is used by every SQL backend. created_at is declared TIMESTAMPTZ so the comparison with the database-side NOW() expression is always timezone-consistent on Postgres.

func (*TokenLockStore) Close

func (db *TokenLockStore) Close() error

func (*TokenLockStore) CreateSchema

func (db *TokenLockStore) CreateSchema() error

func (*TokenLockStore) GetSchema

func (db *TokenLockStore) GetSchema() string

func (*TokenLockStore) ListLocks added in v0.24.0

func (db *TokenLockStore) ListLocks(ctx context.Context) ([]driver.LockRecord, error)

ListLocks returns every currently held lock, joined with the status of its consuming transaction. It reuses the same TokenLocks/Requests join as IsStaleLock/Cleanup, so the notion of "consuming transaction" stays consistent across the diagnostic reader and the actual expiry logic. See driver.TokenLockStore.ListLocks and #2395.

func (*TokenLockStore) Lock

func (db *TokenLockStore) Lock(ctx context.Context, tokenID *token.ID, consumerTxID transaction.ID, walletID string) error

Lock locks the token for consumerTxID. walletID identifies the wallet the tokens are selected for; this SQL-backed store does not apply per-wallet rate limiting and so ignores it. A custom TokenLockStore may use walletID to throttle per wallet.

func (*TokenLockStore) LockAt added in v0.18.0

func (db *TokenLockStore) LockAt(ctx context.Context, tokenID *token.ID, consumerTxID transaction.ID, _ string, createdAt time.Time) error

LockAt is like Lock but records the supplied timestamp as the lock creation time instead of the current time. It is intended for testing (backdating locks to exercise the lease-age expiry path without sleeping).

The lock row is inserted only if the token is still spendable at that instant, in the same statement, so the two outcomes a caller has to tell apart cannot interleave: a primary-key conflict means somebody else holds the lock (ErrTokenAlreadyLocked), and a zero-row insert means the token is no longer a valid candidate (ErrTokenNotSpendable). The foreign key alone is not enough for the second: it only requires the token row to exist, and spending a token soft-deletes it rather than removing the row, so without the predicate a lock can be acquired on a token that was spent since the caller read it - after which the caller returns it to a consumer that cannot load it. See #2395.

func (*TokenLockStore) UnlockByTxID

func (db *TokenLockStore) UnlockByTxID(ctx context.Context, consumerTxID transaction.ID) error

type TokenStore

type TokenStore struct {
	// contains filtered or unexported fields
}

func NewTokenStoreWithNotifier

func NewTokenStoreWithNotifier(readDB *sql.DB, writeDB WriteDB, tables TableNames, ci common3.CondInterpreter, notifier driver.TokenNotifier) (*TokenStore, error)

func NewTokenStoreWithNotifierAndCleanup

func NewTokenStoreWithNotifierAndCleanup(
	readDB *sql.DB,
	writeDB WriteDB,
	tables TableNames,
	ci common3.CondInterpreter,
	notifier driver.TokenNotifier,
	cleanupLeaderFactory func(context.Context, WriteDB) (driver.CleanupLeadership, bool, error),
) (*TokenStore, error)

func (*TokenStore) AcquireCleanupLeadership

func (db *TokenStore) AcquireCleanupLeadership(ctx context.Context) (driver.CleanupLeadership, bool, error)

AcquireCleanupLeadership attempts to acquire leadership for keystore cleanup operations. When no leader factory is configured, leadership is granted locally (for non-distributed deployments). In distributed deployments (PostgreSQL), this uses advisory locks to ensure only one instance performs cleanup. AcquireCleanupLeadership returns a leadership handle for cleanup sweeping. When no leader factory is configured, leadership is granted locally.

func (*TokenStore) Balance

func (db *TokenStore) Balance(ctx context.Context, walletID string, typ token.Type) (*big.Int, error)

Balance returns the sum of the amounts of the tokens with type and EID equal to those passed as arguments. The result is returned as a *big.Int to support arbitrary precision and prevent overflow.

func (*TokenStore) Close

func (db *TokenStore) Close() error

func (*TokenStore) ContinueTokenDBTransaction

func (db *TokenStore) ContinueTokenDBTransaction(tx driver.Transaction) (driver.TokenStoreTransaction, error)

func (*TokenStore) CreateSchema

func (db *TokenStore) CreateSchema() error

func (*TokenStore) DeleteTokens

func (db *TokenStore) DeleteTokens(ctx context.Context, deletedBy string, ids ...*token.ID) error

DeleteTokens deletes multiple tokens at the same time (when spent, invalid or expired)

func (*TokenStore) ExistsCertification

func (db *TokenStore) ExistsCertification(ctx context.Context, tokenID *token.ID) bool

func (*TokenStore) GetAllTokenInfos

func (db *TokenStore) GetAllTokenInfos(ctx context.Context, ids []*token.ID) ([][]byte, error)

GetAllTokenInfos retrieves the token information for the passed ids.

func (*TokenStore) GetCertifications

func (db *TokenStore) GetCertifications(ctx context.Context, ids []*token.ID) ([][]byte, error)

func (*TokenStore) GetDeletedTokensPendingSKICleanup

func (db *TokenStore) GetDeletedTokensPendingSKICleanup(ctx context.Context, olderThan time.Duration, limit int) ([]driver.DeletedToken, error)

GetDeletedTokensPendingSKICleanup returns deleted, owned tokens older than the specified duration that haven't had their SKI keys cleaned up yet (no record in token_ski_cleanups table). Only tokens owned by this node are considered, since this node only holds the secret keys for tokens it owns, not for tokens it merely audited or issued.

func (*TokenStore) GetSchema

func (db *TokenStore) GetSchema() string

func (*TokenStore) GetTokenMetadata

func (db *TokenStore) GetTokenMetadata(ctx context.Context, ids []*token.ID) ([][]byte, error)

GetTokenMetadata retrieves the token metadata for the passed ids. For each id, the callback is invoked to unmarshal the token metadata

func (*TokenStore) GetTokenOutputs

func (db *TokenStore) GetTokenOutputs(ctx context.Context, ids []*token.ID, callback tdriver.QueryCallbackFunc) error

func (*TokenStore) GetTokenOutputsAndMeta

func (db *TokenStore) GetTokenOutputsAndMeta(ctx context.Context, ids []*token.ID) ([][]byte, [][]byte, []token.Format, error)

GetTokenOutputsAndMeta retrieves both the token output, metadata, and type for the passed ids.

func (*TokenStore) GetTokens

func (db *TokenStore) GetTokens(ctx context.Context, inputs ...*token.ID) ([]*token.Token, error)

GetTokens returns the owned tokens and their identifier keys for the passed ids.

func (*TokenStore) HasEnoughSpendableTokens added in v0.26.0

func (db *TokenStore) HasEnoughSpendableTokens(ctx context.Context, walletID string, typ token.Type, target *big.Int) (bool, error)

HasEnoughSpendableTokens reports whether the wallet's total spendable balance of typ is at least target. It deliberately ignores locks: the question it answers is "can this wallet ever pay", not "can it pay right now". The selector uses it as a fast fail, so that a wallet holding dust that could never cover the requested amount fails immediately instead of burning the immediate-retry/backoff budget first.

func (*TokenStore) HasTokenForIdentity

func (db *TokenStore) HasTokenForIdentity(ctx context.Context, ownerIdentity []byte, ownerType string) (bool, error)

HasTokenForIdentity returns true if any token in the Tokens table has the given owner identity and owner type, regardless of whether the token has been spent or not.

func (*TokenStore) IsMine

func (db *TokenStore) IsMine(ctx context.Context, txID string, index uint64) (bool, error)

IsMine just checks if the token is in the local storage and not deleted

func (*TokenStore) IssuedBalance added in v0.15.1

func (db *TokenStore) IssuedBalance(ctx context.Context, opts tdriver.IssuerBalanceQuery) (*big.Int, error)

IssuedBalance returns the sum of the amounts of the tokens issued by this node, filtered by the passed options. Redeemed tokens are excluded. The result is returned as a *big.Int to support arbitrary precision and prevent overflow.

func (*TokenStore) ListAuditTokens

func (db *TokenStore) ListAuditTokens(ctx context.Context, ids ...*token.ID) ([]*token.Token, error)

ListAuditTokens returns the audited tokens associated to the passed ids

func (*TokenStore) ListHistoryIssuedTokens

func (db *TokenStore) ListHistoryIssuedTokens(ctx context.Context) (*token.IssuedTokens, error)

ListHistoryIssuedTokens returns the list of issued tokens

func (*TokenStore) ListUnspentTokens

func (db *TokenStore) ListUnspentTokens(ctx context.Context) (*token.UnspentTokens, error)

ListUnspentTokens returns the list of unspent tokens

func (*TokenStore) ListUnspentTokensBy

func (db *TokenStore) ListUnspentTokensBy(ctx context.Context, walletID string, typ token.Type) (*token.UnspentTokens, error)

ListUnspentTokensBy returns the list of unspent tokens, filtered by owner and token type

func (*TokenStore) ListUnspentTokensByWallets

func (db *TokenStore) ListUnspentTokensByWallets(ctx context.Context, walletIDs []string, typ token.Type) (map[string]*token.UnspentTokens, error)

ListUnspentTokensByWallets issues a single SELECT with an IN clause on the owning wallet, then partitions the result rows in Go. This avoids the N round-trip cost of calling ListUnspentTokensBy in a loop when a caller (e.g. a bank-node wallet list endpoint) needs balances for many wallets at once. Empty input returns an empty map without querying.

func (*TokenStore) MarkTokenCleaned

func (db *TokenStore) MarkTokenCleaned(ctx context.Context, txID string, index uint64, cleanedBy string) error

MarkTokenCleaned marks a token as having its cryptographic keys cleaned up. This prevents the cleanup service from processing the same token multiple times. The cleanedBy parameter identifies which instance performed the cleanup.

func (*TokenStore) NewTokenDBTransaction

func (db *TokenStore) NewTokenDBTransaction() (driver.TokenStoreTransaction, error)

func (*TokenStore) Notifier

func (db *TokenStore) Notifier() (driver.TokenNotifier, error)

func (*TokenStore) PreparedStmtCount added in v0.15.1

func (db *TokenStore) PreparedStmtCount() int

PreparedStmtCount returns the number of cached prepared statements for UnspentTokensIteratorBy. Intended for tests verifying statement reuse across argument shapes.

func (*TokenStore) PublicParams

func (db *TokenStore) PublicParams(ctx context.Context) ([]byte, error)

func (*TokenStore) PublicParamsByHash

func (db *TokenStore) PublicParamsByHash(ctx context.Context, rawHash tdriver.PPHash) ([]byte, error)

PublicParamsByHash returns the public parameters whose hash matches the passed one, or nil if none are stored under it.

Verification: the returned parameters are hashed and compared against rawHash — see integrity.CheckPublicParamsHash. Callers fetch by hash in order to re-validate a transaction against the setup it was created under, so parameters that do not hash to the requested hash are reported rather than returned.

func (*TokenStore) PublicParamsHashes added in v0.21.0

func (db *TokenStore) PublicParamsHashes(ctx context.Context) ([]tdriver.PPHash, error)

PublicParamsHashes returns the hashes of every public parameters version stored so far, most recently stored first. StorePublicParams never overwrites an existing row, so this is the full history of public parameters this node has observed.

func (*TokenStore) QuerySpendableTokens added in v0.24.0

func (db *TokenStore) QuerySpendableTokens(ctx context.Context, params driver.SpendableTokensQuery) (tdriver.SpendableTokensIterator, error)

QuerySpendableTokens returns an iterator over the spendable tokens matching params.

func (*TokenStore) QueryTokenDetails

func (db *TokenStore) QueryTokenDetails(ctx context.Context, params driver.QueryTokenDetailsParams) ([]driver.TokenDetails, error)

QueryTokenDetails returns details about owned tokens, regardless if they have been spent or not. Filters work cumulatively and may be left empty. If a token is owned by two enrollmentIDs and there is no filter on enrollmentID, the token will be returned twice (once for each owner).

func (*TokenStore) RedeemedBalance added in v0.15.1

func (db *TokenStore) RedeemedBalance(ctx context.Context, opts tdriver.IssuerBalanceQuery) (*big.Int, error)

RedeemedBalance returns the sum of the amounts of the tokens redeemed against an issuer known to this node, filtered by the passed options. The result is returned as a *big.Int to support arbitrary precision and prevent overflow.

func (*TokenStore) SetSupportedTokenFormats

func (db *TokenStore) SetSupportedTokenFormats(formats []token.Format) error

func (*TokenStore) SpendableTokensIteratorBy

func (db *TokenStore) SpendableTokensIteratorBy(ctx context.Context, walletID string, typ token.Type) (tdriver.SpendableTokensIterator, error)

SpendableTokensIteratorBy returns the minimum information about the tokens needed for the selector: every spendable token of the wallet and type that no other consumer currently holds a lock on, ascending by amount and unlimited. Callers that need only part of that set should use QuerySpendableTokens.

func (*TokenStore) StoreCertifications

func (db *TokenStore) StoreCertifications(ctx context.Context, certifications map[*token.ID][]byte) error

func (*TokenStore) StorePublicParams

func (db *TokenStore) StorePublicParams(ctx context.Context, raw []byte) error

StorePublicParams stores the passed public parameters, keyed by their hash, if they are not stored already.

A read failure is propagated rather than swallowed: PublicParamsByHash recomputes the hash of what it finds, so it is the one place where a row whose raw and raw_hash columns disagree is detectable. Falling through to the INSERT would turn that into an opaque primary-key violation and leave the operator without the reason.

func (*TokenStore) StoreToken

func (db *TokenStore) StoreToken(ctx context.Context, tr driver.TokenRecord, owners []string) (err error)

func (*TokenStore) TransactionExists

func (db *TokenStore) TransactionExists(ctx context.Context, id string) (bool, error)

func (*TokenStore) UnspentLedgerTokensIteratorBy

func (db *TokenStore) UnspentLedgerTokensIteratorBy(ctx context.Context) (tdriver.LedgerTokensIterator, error)

UnspentLedgerTokensIteratorBy returns an iterator over all unspent ledger tokens

func (*TokenStore) UnspentTokensIterator

func (db *TokenStore) UnspentTokensIterator(ctx context.Context) (tdriver.UnspentTokensIterator, error)

UnspentTokensIterator returns an iterator over all unspent tokens

func (*TokenStore) UnspentTokensIteratorBy

func (db *TokenStore) UnspentTokensIteratorBy(ctx context.Context, walletID string, tokenType token.Type) (tdriver.UnspentTokensIterator, error)

func (*TokenStore) UnsupportedTokensIteratorBy

func (db *TokenStore) UnsupportedTokensIteratorBy(ctx context.Context, walletID string, tokenType token.Type) (tdriver.UnsupportedTokensIterator, error)

UnsupportedTokensIteratorBy returns the minimum information for upgrade about the tokens that are not supported

func (*TokenStore) WhoDeletedTokens

func (db *TokenStore) WhoDeletedTokens(ctx context.Context, inputs ...*token.ID) ([]string, []bool, error)

WhoDeletedTokens returns information about which transaction deleted the passed tokens. The bool array is an indicator used to tell if the token at a given position has been deleted or not

type TokenTransaction

type TokenTransaction struct {
	// contains filtered or unexported fields
}

func (*TokenTransaction) Commit

func (t *TokenTransaction) Commit() error

func (*TokenTransaction) Delete

func (t *TokenTransaction) Delete(ctx context.Context, tokenID token.ID, deletedBy string) error

func (*TokenTransaction) GetToken

func (t *TokenTransaction) GetToken(ctx context.Context, tokenID token.ID, includeDeleted bool) (*token.Token, []string, error)

func (*TokenTransaction) Rollback

func (t *TokenTransaction) Rollback() error

func (*TokenTransaction) SetSpendable

func (t *TokenTransaction) SetSpendable(ctx context.Context, tokenID token.ID, spendable bool) error

func (*TokenTransaction) SetSpendableBySupportedTokenFormats

func (t *TokenTransaction) SetSpendableBySupportedTokenFormats(ctx context.Context, formats []token.Format) error

SetSpendableBySupportedTokenFormats reconciles the spendable flag of every token against formats: tokens whose ledger type is supported become spendable, all others become non-spendable.

Both updates are conditional on the flag actually having to change, so a reconciliation that agrees with the stored state writes no rows. Rewriting the whole table unconditionally - as this used to do, by clearing every flag before setting the supported ones - costs a full-table rewrite and the attendant MVCC bloat on every call, however little has changed.

func (*TokenTransaction) StoreToken

func (t *TokenTransaction) StoreToken(ctx context.Context, tr driver.TokenRecord, owners []string) error

type TransactionStore

type TransactionStore struct {
	// contains filtered or unexported fields
}

func NewAuditTransactionStore

func NewAuditTransactionStore(readDB *sql.DB, writeDB WriteDB, tables TableNames, ci common3.CondInterpreter, pi common3.PagInterpreter) (*TransactionStore, error)

func NewOwnerTransactionStore

func NewOwnerTransactionStore(readDB *sql.DB, writeDB WriteDB, tables TableNames, ci common3.CondInterpreter, pi common3.PagInterpreter) (*TransactionStore, error)

func NewTransactionStoreWithNotifierAndRecovery

func NewTransactionStoreWithNotifierAndRecovery(
	readDB *sql.DB,
	writeDB WriteDB,
	tables TableNames,
	ci common3.CondInterpreter,
	pi common3.PagInterpreter,
	notifier dbdriver.TransactionNotifier,
	recoveryLeaderFactory func(context.Context, WriteDB) (dbdriver.RecoveryLeadership, bool, error),
) (*TransactionStore, error)

func (*TransactionStore) AcquireLeadership added in v0.24.0

AcquireLeadership returns a leadership handle for an arbitrary caller-chosen lockID, independent of the recovery lock bound at construction. It exists for callers that need their own leader election - such as the ledger drift checks sweep - so they never contend with the recovery sweep for the same lock. The default implementation grants leadership locally: backends that support real distributed locking (e.g. postgres) override it.

func (*TransactionStore) AcquireRecoveryLeadership

func (db *TransactionStore) AcquireRecoveryLeadership(ctx context.Context) (dbdriver.RecoveryLeadership, bool, error)

AcquireRecoveryLeadership returns a leadership handle for recovery sweeping. When no leader factory is configured, leadership is granted locally.

func (*TransactionStore) AddTransactionEndorsementAck

func (db *TransactionStore) AddTransactionEndorsementAck(ctx context.Context, txID string, endorser token.Identity, sigma []byte) (err error)

AddTransactionEndorsementAck records the signature of endorser over the payload it was sent for txID.

Verification: the signature cannot be verified here — the payload it was produced over is not persisted, so the caller is the last holder of it. This layer refuses a vacuous acknowledgement: one with no endorser identity, which would collide with every other such row when read back into a map keyed by endorser, or no signature, which would be indistinguishable from a genuine acknowledgement to every consumer.

func (*TransactionStore) ClaimPendingTransactions

func (db *TransactionStore) ClaimPendingTransactions(ctx context.Context, params dbdriver.RecoveryClaimParams) ([]*dbdriver.RecoveryClaim, error)

ClaimPendingTransactions returns a claimed batch of Pending transactions. The default SQL implementation is permissive and does not persist recovery claims. tx_id and stored_at are projected directly from the requests table — the transactions table is no longer joined since it carries no information the recovery loop needs and adding it would re-introduce a fan-out by movement/output that the caller would have to dedupe by tx_id.

func (*TransactionStore) Close

func (db *TransactionStore) Close() error

func (*TransactionStore) CountOpenFindings added in v0.24.0

func (db *TransactionStore) CountOpenFindings(ctx context.Context) (map[int]int64, error)

CountOpenFindings returns how many open findings there currently are, per severity. Aggregated in SQL rather than fetched row by row and counted in Go: a node with a large-scale, genuinely open incident is exactly the case this must stay cheap for.

func (*TransactionStore) CreateSchema

func (db *TransactionStore) CreateSchema() error

func (*TransactionStore) GetSchema

func (db *TransactionStore) GetSchema() string

func (*TransactionStore) GetStatus

func (db *TransactionStore) GetStatus(ctx context.Context, txID string) (dbdriver.TxStatus, string, error)

func (*TransactionStore) GetStatuses added in v0.15.1

func (db *TransactionStore) GetStatuses(ctx context.Context, txIDs []string) (map[string]dbdriver.TxStatus, error)

GetStatuses fetches the status for the given tx ids in a single SELECT. Missing tx ids are absent from the returned map — callers should treat a missing key identically to GetStatus returning Unknown. Empty input returns an empty map without querying.

func (*TransactionStore) GetTokenRequest

func (db *TransactionStore) GetTokenRequest(ctx context.Context, txID string) ([]byte, error)

func (*TransactionStore) GetTokenRequests

func (db *TransactionStore) GetTokenRequests(ctx context.Context, txIDs []string) (map[string][]byte, error)

GetTokenRequests fetches the token requests for the given tx ids in a single SELECT. Missing tx ids are absent from the returned map — callers should treat a missing key identically to GetTokenRequest returning nil. Empty input returns an empty map without querying.

func (*TransactionStore) GetTransactionEndorsementAcks

func (db *TransactionStore) GetTransactionEndorsementAcks(ctx context.Context, txID string) (map[string][]byte, error)

func (*TransactionStore) NewTransactionStoreTransaction

func (db *TransactionStore) NewTransactionStoreTransaction() (dbdriver.TransactionStoreTransaction, error)

func (*TransactionStore) Notifier

func (*TransactionStore) PrefixedTableName

func (s *TransactionStore) PrefixedTableName(name string) string

PrefixedTableName returns the formatted table name for the given logical name. It falls back to the unformatted name if the prefix or the table parameters cannot produce a valid identifier, rather than panicking: the store is already constructed at this point and a crash here would take the node down.

func (*TransactionStore) QueryFindings added in v0.24.0

QueryFindings returns the stored findings matching params, worst first.

func (*TransactionStore) QueryMovements

func (db *TransactionStore) QueryMovements(ctx context.Context, params dbdriver.QueryMovementsParams) (res []*dbdriver.MovementRecord, err error)

func (*TransactionStore) QueryTokenRequests

QueryTokenRequests returns an iterator over the token requests matching the passed params.

func (*TransactionStore) QueryTransactions

func (*TransactionStore) ReleaseRecoveryClaim

func (db *TransactionStore) ReleaseRecoveryClaim(context.Context, string, string, string) error

ReleaseRecoveryClaim clears persisted recovery claim metadata and stores the recovery message. The default SQL implementation is a no-op.

func (*TransactionStore) ResolveFindingsNotSeenSince added in v0.24.0

func (db *TransactionStore) ResolveFindingsNotSeenSince(ctx context.Context, checkers []string, seenAt time.Time) (int64, error)

ResolveFindingsNotSeenSince closes the open findings of the passed checkers that the latest sweep did not observe.

func (*TransactionStore) SetStatus

func (db *TransactionStore) SetStatus(ctx context.Context, txID string, status dbdriver.TxStatus, message string) error

func (*TransactionStore) UpsertFindings added in v0.24.0

func (db *TransactionStore) UpsertFindings(ctx context.Context, findings []dbdriver.FindingRecord, seenAt time.Time) error

UpsertFindings records the findings observed by one sweep. See the driver interface for the aging semantics.

type TransactionStoreTransaction

type TransactionStoreTransaction struct {
	// contains filtered or unexported fields
}

func (*TransactionStoreTransaction) AddMovement

func (*TransactionStoreTransaction) AddTokenRequest

func (w *TransactionStoreTransaction) AddTokenRequest(ctx context.Context, txID string, tr []byte, applicationMetadata, publicMetadata map[string][]byte, ppHash driver2.PPHash) error

AddTokenRequest binds txID to the serialized token request tr.

Verification: this layer is a byte store and does not interpret tr — the wire-format and anchor checks live in the ttxdb and auditdb services, which know which of the two token request formats they hold. What is refused here is a row that no later check could act on: an empty transaction id, empty request bytes, or an empty public parameters hash. See docs/security/store_integrity_verification.md.

func (*TransactionStoreTransaction) AddTransaction

func (*TransactionStoreTransaction) Commit

func (w *TransactionStoreTransaction) Commit() error

func (*TransactionStoreTransaction) Impl

func (*TransactionStoreTransaction) Rollback

func (w *TransactionStoreTransaction) Rollback()

func (*TransactionStoreTransaction) SetStatus

func (w *TransactionStoreTransaction) SetStatus(ctx context.Context, txID string, status dbdriver.TxStatus, message string) error

type WalletStore

type WalletStore struct {
	// contains filtered or unexported fields
}

func NewWalletStore

func NewWalletStore(readDB *sql.DB, writeDB WriteDB, tables TableNames, ci common3.CondInterpreter) (*WalletStore, error)

func (*WalletStore) Close

func (db *WalletStore) Close() error

func (*WalletStore) CreateSchema

func (db *WalletStore) CreateSchema() error

func (*WalletStore) GetConfID added in v0.15.1

func (db *WalletStore) GetConfID(ctx context.Context, identity token.Identity) (string, error)

func (*WalletStore) GetSchema

func (db *WalletStore) GetSchema() string

func (*WalletStore) GetWalletID

func (db *WalletStore) GetWalletID(ctx context.Context, identity token.Identity, roleID int) (driver.WalletID, error)

func (*WalletStore) GetWalletIDs

func (db *WalletStore) GetWalletIDs(ctx context.Context, roleID int) ([]driver.WalletID, error)

func (*WalletStore) IdentityExists

func (db *WalletStore) IdentityExists(ctx context.Context, identity token.Identity, wID driver.WalletID, roleID int) (bool, error)

IdentityExists reports whether the identity-wallet-role binding has been stored. A lookup failure is returned as an error rather than as "not found": the two are not interchangeable, and collapsing them lets a transient connectivity problem read as a definitive negative.

func (*WalletStore) LoadMeta

func (db *WalletStore) LoadMeta(ctx context.Context, identity token.Identity, wID driver.WalletID, roleID int) ([]byte, error)

func (*WalletStore) StoreIdentity

func (db *WalletStore) StoreIdentity(ctx context.Context, identity token.Identity, eID string, wID driver.WalletID, roleID int, meta []byte, confID string) error

type WriteDB added in v0.24.0

type WriteDB interface {
	fscsql.WriteDB

	// Conn returns a single dedicated connection, held until it is closed.
	Conn(ctx context.Context) (*sql.Conn, error)
}

WriteDB is the write handle a store executes its statements through. It is FSC's write surface (Begin, BeginTx, Exec, ExecContext, Close) plus Conn, which Postgres needs to pin a session for the duration of an advisory lock.

*sql.DB satisfies it, so this is the type the stores take instead of the concrete pool: it lets a backend decorate the write path without every store knowing about it. SQLite does exactly that, wrapping the pool so a statement rejected with SQLITE_BUSY is retried instead of surfacing to the caller.

Only the statement methods are decorated. Begin and BeginTx hand out a raw *sql.Tx whose statements go straight to the driver, so a transactional write still sees SQLITE_BUSY - a retry there would have to replay the whole transaction, which is the caller's decision, not the pool's.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL