database

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: May 28, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ErrGetAccountBalance = "failed to get account balance"
	ErrRecordLedgerEntry = "failed to record a ledger entry"
)
View Source
const (
	DefaultLimit = 10
	MaxLimit     = 100
)

Variables

View Source
var (
	ErrInvalidLedgerTransactionType = "invalid ledger transaction type"
	ErrRecordTransaction            = "failed to record transaction"
)
View Source
var ErrEventHasAlreadyBeenProcessed = errors.New("contract event has already been processed")
View Source
var ErrSessionKeyNotAllowed = errors.New("session key not allowed")

ErrSessionKeyNotAllowed is returned by LockSessionKeyState when the session key for the requested kind is bound to a wallet other than the submitter. The message is intentionally generic so the API does not confirm whether a given session_key is registered elsewhere.

Functions

func ConnectToDB

func ConnectToDB(cnf DatabaseConfig, embedMigrations embed.FS) (*gorm.DB, error)

func RegisterDBStatsCollector

func RegisterDBStatsCollector(db *gorm.DB, reg prometheus.Registerer) error

RegisterDBStatsCollector registers the standard go_sql_* collector on reg for the underlying *sql.DB, so pool-state gauges and wait counters become scrapable. Returns an error if the gorm DB doesn't expose a *sql.DB (sqlite-in-memory variants normally do; misconfigured pools don't).

Emits, under "nitronode" db name:

go_sql_max_open_connections
go_sql_open_connections
go_sql_in_use_connections
go_sql_idle_connections
go_sql_wait_count_total
go_sql_wait_duration_seconds_total
go_sql_max_idle_closed_total
go_sql_max_idle_time_closed_total
go_sql_max_lifetime_closed_total

func RegisterMetricsCallbacks

func RegisterMetricsCallbacks(db *gorm.DB, obs QueryDurationObserver) error

RegisterMetricsCallbacks installs gorm callbacks that observe wall-clock duration of each Create / Query / Update / Delete / Row / Raw operation onto obs. Pass nil to skip registration (test / sqlite-in-memory cases).

The callbacks add no per-call allocation beyond a single time.Time stashed in the gorm context dict.

func SetupTestDB

func SetupTestDB(t testing.TB) (*gorm.DB, func())

SetupTestDB chooses SQLite or Postgres based on TEST_DB_DRIVER environment variable. This is exported so it can be used by tests in this package.

Types

type ActionLogEntryV1

type ActionLogEntryV1 struct {
	ID          uuid.UUID `gorm:"type:char(36);primaryKey"`
	UserWallet  string    `gorm:"column:user_wallet;not null"`
	GatedAction uint8     `gorm:"column:gated_action;not null"`
	CreatedAt   time.Time
}

func (ActionLogEntryV1) TableName

func (ActionLogEntryV1) TableName() string

type ActiveCountByLabel

type ActiveCountByLabel struct {
	Label string `gorm:"column:label"`
	Count uint64 `gorm:"column:count"`
}

CountActiveUsers returns the number of distinct users who had channel state updates within the given duration, grouped by asset. If asset is empty, counts across all assets. ActiveCountByLabel holds a count grouped by a label (asset or application_id).

type AppLedgerEntryV1

type AppLedgerEntryV1 struct {
	ID          uuid.UUID       `gorm:"type:char(36);primaryKey"`
	AccountID   string          `gorm:"column:account_id;not null;index:idx_account_asset_symbol;index:idx_account_wallet"`
	AssetSymbol string          `gorm:"column:asset_symbol;not null;index:idx_account_asset_symbol"`
	Wallet      string          `gorm:"column:wallet;not null;index:idx_account_wallet"`
	Credit      decimal.Decimal `gorm:"column:credit;type:varchar(78);not null"`
	Debit       decimal.Decimal `gorm:"column:debit;type:varchar(78);not null"`
	CreatedAt   time.Time
}

AppLedgerEntryV1 represents a ledger entry in the database

func (AppLedgerEntryV1) TableName

func (AppLedgerEntryV1) TableName() string

type AppParticipantV1

type AppParticipantV1 struct {
	AppSessionID    string `gorm:"column:app_session_id;not null;primaryKey;priority:1"`
	WalletAddress   string `gorm:"column:wallet_address;not null;primaryKey;priority:2"`
	SignatureWeight uint8  `gorm:"column:signature_weight;not null"`
}

AppParticipantV1 represents the definition for an app participant.

func (AppParticipantV1) TableName

func (AppParticipantV1) TableName() string

type AppSessionCount

type AppSessionCount struct {
	Application string               `gorm:"column:application_id"`
	Status      app.AppSessionStatus `gorm:"column:status"`
	Count       uint64               `gorm:"column:count"`
}

AppSessionCount holds the result of a COUNT() GROUP BY query on app sessions.

type AppSessionKeyAppSessionIDV1

type AppSessionKeyAppSessionIDV1 struct {
	SessionKeyStateID string `gorm:"column:session_key_state_id;not null;primaryKey;priority:1"`
	AppSessionID      string `gorm:"column:app_session_id;not null;primaryKey;priority:2;index"`
}

AppSessionKeyAppSessionIDV1 links a session key state to an app session ID.

func (AppSessionKeyAppSessionIDV1) TableName

func (AppSessionKeyAppSessionIDV1) TableName() string

type AppSessionKeyApplicationV1

type AppSessionKeyApplicationV1 struct {
	SessionKeyStateID string `gorm:"column:session_key_state_id;not null;primaryKey;priority:1"`
	ApplicationID     string `gorm:"column:application_id;not null;primaryKey;priority:2;index"`
}

SessionKeyApplicationV1 links a session key state to an application ID.

func (AppSessionKeyApplicationV1) TableName

func (AppSessionKeyApplicationV1) TableName() string

type AppSessionKeyStateV1

type AppSessionKeyStateV1 struct {
	ID             string                        `gorm:"column:id;primaryKey"`
	UserAddress    string                        `gorm:"column:user_address;not null;uniqueIndex:idx_session_key_states_v1_user_key_ver,priority:1"`
	SessionKey     string                        `gorm:"column:session_key;not null;uniqueIndex:idx_session_key_states_v1_user_key_ver,priority:2"`
	Version        uint64                        `gorm:"column:version;not null;uniqueIndex:idx_session_key_states_v1_user_key_ver,priority:3"`
	ApplicationIDs []AppSessionKeyApplicationV1  `gorm:"foreignKey:SessionKeyStateID;references:ID"`
	AppSessionIDs  []AppSessionKeyAppSessionIDV1 `gorm:"foreignKey:SessionKeyStateID;references:ID"`
	ExpiresAt      time.Time                     `gorm:"column:expires_at;not null"`
	UserSig        string                        `gorm:"column:user_sig;not null"`
	SessionKeySig  string                        `gorm:"column:session_key_sig"`
	CreatedAt      time.Time
}

AppSessionKeyStateV1 represents a session key state in the database. ID is Hash(user_address + session_key + version).

func (AppSessionKeyStateV1) TableName

func (AppSessionKeyStateV1) TableName() string

type AppSessionV1

type AppSessionV1 struct {
	ID            string               `gorm:"primaryKey"`
	ApplicationID string               `gorm:"column:application_id;not null"`
	Nonce         uint64               `gorm:"column:nonce;not null"`
	Participants  []AppParticipantV1   `gorm:"foreignKey:AppSessionID;references:ID"`
	SessionData   string               `gorm:"column:session_data;type:text;not null"`
	Quorum        uint8                `gorm:"column:quorum;default:100"`
	Version       uint64               `gorm:"column:version;default:1"`
	Status        app.AppSessionStatus `gorm:"column:status;not null"`
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

AppSessionV1 represents a virtual payment application session between participants

func (AppSessionV1) TableName

func (AppSessionV1) TableName() string

type AppV1

type AppV1 struct {
	ID                          string `gorm:"primaryKey"`
	OwnerWallet                 string `gorm:"column:owner_wallet;not null"`
	Metadata                    string `gorm:"column:metadata;type:text;not null"`
	Version                     uint64 `gorm:"column:version;default:1"`
	CreationApprovalNotRequired bool   `gorm:"column:creation_approval_not_required"`
	CreatedAt                   time.Time
	UpdatedAt                   time.Time
}

AppV1 represents an application registry entry in the database.

func (AppV1) TableName

func (AppV1) TableName() string

type BlockchainAction

type BlockchainAction struct {
	ID           int64                  `gorm:"primary_key"`
	Type         BlockchainActionType   `gorm:"column:action_type;not null"`
	StateID      string                 `gorm:"column:state_id;size:66"`
	BlockchainID uint64                 `gorm:"column:blockchain_id;not null"`
	Data         datatypes.JSON         `gorm:"column:action_data;type:text"`
	Status       BlockchainActionStatus `gorm:"column:status;not null"`
	Retries      uint8                  `gorm:"column:retry_count;default:0"`
	Error        string                 `gorm:"column:last_error;type:text"`
	TxHash       string                 `gorm:"column:transaction_hash;size:66"`
	CreatedAt    time.Time              `gorm:"column:created_at"`
	UpdatedAt    time.Time              `gorm:"column:updated_at"`
}

func (BlockchainAction) TableName

func (BlockchainAction) TableName() string

type BlockchainActionStatus

type BlockchainActionStatus uint8
const (
	BlockchainActionStatusPending BlockchainActionStatus = iota
	BlockchainActionStatusCompleted
	BlockchainActionStatusFailed
)

type BlockchainActionType

type BlockchainActionType uint8
const (
	ActionTypeCheckpoint BlockchainActionType = 1
	ActionTypeChallenge  BlockchainActionType = 2

	ActionTypeInitiateEscrowDeposit BlockchainActionType = 10
	ActionTypeFinalizeEscrowDeposit BlockchainActionType = 11

	ActionTypeInitiateEscrowWithdrawal BlockchainActionType = 20
	ActionTypeFinalizeEscrowWithdrawal BlockchainActionType = 21
)

func (BlockchainActionType) String

func (t BlockchainActionType) String() string

type Channel

type Channel struct {
	ChannelID             string             `gorm:"column:channel_id;primaryKey;"`
	UserWallet            string             `gorm:"column:user_wallet;not null"`
	Asset                 string             `gorm:"column:asset;not null"`
	Type                  core.ChannelType   `gorm:"column:type;not null"`
	BlockchainID          uint64             `gorm:"column:blockchain_id;not null"`
	Token                 string             `gorm:"column:token;not null"`
	ChallengeDuration     uint32             `gorm:"column:challenge_duration;not null"`
	ChallengeExpiresAt    *time.Time         `gorm:"column:challenge_expires_at;default:null"`
	Nonce                 uint64             `gorm:"column:nonce;not null;"`
	ApprovedSigValidators string             `gorm:"column:approved_sig_validators;not null;"`
	Status                core.ChannelStatus `gorm:"column:status;not null;"`
	StateVersion          uint64             `gorm:"column:state_version;not null;"`
	CreatedAt             time.Time
	UpdatedAt             time.Time
}

Channel represents a state channel between participants

func (Channel) TableName

func (Channel) TableName() string

TableName specifies the table name for the Channel model

type ChannelCount

type ChannelCount struct {
	Asset  string             `gorm:"column:asset"`
	Status core.ChannelStatus `gorm:"column:status"`
	Count  uint64             `gorm:"column:count"`
}

ChannelCount holds the result of a COUNT() GROUP BY query on channels.

type ChannelSessionKeyAssetV1

type ChannelSessionKeyAssetV1 struct {
	SessionKeyStateID string `gorm:"column:session_key_state_id;not null;primaryKey;priority:1"`
	Asset             string `gorm:"column:asset;not null;primaryKey;priority:2;index"`
}

ChannelSessionKeyAssetV1 links a channel session key state to an asset.

func (ChannelSessionKeyAssetV1) TableName

func (ChannelSessionKeyAssetV1) TableName() string

type ChannelSessionKeyStateV1

type ChannelSessionKeyStateV1 struct {
	ID            string                     `gorm:"column:id;primaryKey"`
	UserAddress   string                     `gorm:"column:user_address;not null;uniqueIndex:idx_channel_session_key_states_v1_user_key_ver,priority:1"`
	SessionKey    string                     `gorm:"column:session_key;not null;uniqueIndex:idx_channel_session_key_states_v1_user_key_ver,priority:2"`
	Version       uint64                     `gorm:"column:version;not null;uniqueIndex:idx_channel_session_key_states_v1_user_key_ver,priority:3"`
	Assets        []ChannelSessionKeyAssetV1 `gorm:"foreignKey:SessionKeyStateID;references:ID"`
	MetadataHash  string                     `gorm:"column:metadata_hash;type:char(66);not null"`
	ExpiresAt     time.Time                  `gorm:"column:expires_at;not null"`
	UserSig       string                     `gorm:"column:user_sig;not null"`
	SessionKeySig string                     `gorm:"column:session_key_sig"`
	CreatedAt     time.Time
}

ChannelSessionKeyStateV1 represents a channel session key state in the database.

func (ChannelSessionKeyStateV1) TableName

func (ChannelSessionKeyStateV1) TableName() string

type ContractEvent

type ContractEvent struct {
	ID              int64     `gorm:"primary_key;column:id"`
	ContractAddress string    `gorm:"column:contract_address"`
	BlockchainID    uint64    `gorm:"column:blockchain_id"`
	Name            string    `gorm:"column:name"`
	BlockNumber     uint64    `gorm:"column:block_number"`
	TransactionHash string    `gorm:"column:transaction_hash"`
	LogIndex        uint32    `gorm:"column:log_index"`
	CreatedAt       time.Time `gorm:"column:created_at"`
}

func (ContractEvent) TableName

func (ContractEvent) TableName() string

type CurrentSessionKeyStateV1

type CurrentSessionKeyStateV1 struct {
	UserAddress string         `gorm:"column:user_address;primaryKey;size:42"`
	SessionKey  string         `gorm:"column:session_key;primaryKey;size:42;uniqueIndex:current_session_key_states_v1_key_kind_uniq,priority:1"`
	Kind        SessionKeyKind `gorm:"column:kind;primaryKey;type:smallint;uniqueIndex:current_session_key_states_v1_key_kind_uniq,priority:2"`
	Version     uint64         `gorm:"column:version;not null"`
	UpdatedAt   time.Time      `gorm:"column:updated_at"`
}

CurrentSessionKeyStateV1 is the latest-version pointer per (user_address, session_key, kind). Reads of get_last_key_states JOIN this table to the corresponding history table (channel_session_key_states_v1 or app_session_key_states_v1) on (user_address, session_key, version), bounding per-request DB work to O(distinct keys).

The uniqueIndex on (session_key, kind) mirrors the postgres constraint added by 20260508000000_session_key_ownership_constraints.sql so AutoMigrate (sqlite) enforces the same one-owner-per-key invariant that LockSessionKeyState relies on. The index name matches the postgres constraint name so both paths converge on a single source of truth.

func (CurrentSessionKeyStateV1) TableName

func (CurrentSessionKeyStateV1) TableName() string

type DBStore

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

func (*DBStore) CheckActiveChannel

func (s *DBStore) CheckActiveChannel(wallet, asset string) (string, *core.ChannelStatus, error)

CheckActiveChannel verifies if a user has an active home channel for the given asset and returns its approved signature validators along with the channel status. "Active" includes Void (DB-only, awaiting onchain confirmation) and Open (materialized onchain). This is intentional: non-escrow offchain transitions (transfers, etc.) are permitted before onchain confirmation lands. Callers operating on cross-chain escrow flows that depend on onchain home-channel materialization must check that the returned status is ChannelStatusOpen.

A nil status pointer means no active channel was found.

func (*DBStore) Complete

func (s *DBStore) Complete(actionID int64, txHash string) error

func (*DBStore) CountActiveAppSessions

func (s *DBStore) CountActiveAppSessions(window time.Duration) ([]ActiveCountByLabel, error)

CountActiveAppSessions returns app session counts per application within the given window.

func (*DBStore) CountActiveUsers

func (s *DBStore) CountActiveUsers(window time.Duration) ([]ActiveCountByLabel, error)

CountActiveUsers returns distinct user counts per asset and an "all" aggregate for users with channel state updates within the given window.

func (*DBStore) CountSessionKeysForUser

func (s *DBStore) CountSessionKeysForUser(userAddress string) (uint32, error)

CountSessionKeysForUser returns the number of distinct active session keys recorded for the wallet in the pointer table, across both kinds. Drives the per-user cap at submit time. Rows seeded by LockSessionKeyState (version=0) are excluded so that a failed-cap rejection does not itself leave a phantom row counted toward the cap. Revoked or naturally expired keys (expires_at <= now in the underlying history row) are also excluded so that a revoke frees the slot. A single now is bound for both kind branches so the count is internally consistent.

func (*DBStore) CreateApp

func (s *DBStore) CreateApp(entry app.AppV1) error

CreateApp registers a new application. Returns an error if the app ID already exists.

func (*DBStore) CreateAppSession

func (s *DBStore) CreateAppSession(session app.AppSessionV1) error

CreateAppSession initializes a new application session.

func (*DBStore) CreateChannel

func (s *DBStore) CreateChannel(channel core.Channel) error

CreateChannel creates a new channel entity in the database.

func (*DBStore) EnsureNoOngoingEscrowOperation

func (s *DBStore) EnsureNoOngoingEscrowOperation(wallet, asset string) error

EnsureNoOngoingEscrowOperation validates that the user has no in-flight escrow operation that would prevent the node from issuing receiver-side states (transfer receive, app-session release).

Validation logic by latest signed transition type:

  • escrow_lock / mutual_lock: always considered ongoing (no finalization yet)
  • escrow_deposit: considered settled when the on-chain escrow channel version equals the signed state version (finalize landed). When the chain is exactly one behind, the signed N+1 finalize state lives off-chain and is gated on the escrow channel status: allowed for Open (protocol-intended steady state before the purge queue fires) and Closed (post-purge or post-finalize); blocked for Challenged (on-chain resolution still racing — finalize tx may not land, escrow chain may settle at INITIATE, and replaying N+1 later could violate engine invariants).
  • escrow_withdraw: considered ongoing while the on-chain escrow channel state version has not caught up with the signed state version
  • any other transition: not an escrow operation, allow

func (*DBStore) EnsureNoOngoingStateTransitions

func (s *DBStore) EnsureNoOngoingStateTransitions(wallet, asset string) error

EnsureNoOngoingStateTransitions validates that no conflicting blockchain operations are pending. This method prevents race conditions by ensuring blockchain state versions match the user's last signed state version before accepting new transitions.

Validation logic by transition type:

  • home_deposit: Verify last_state.version == home_channel.state_version
  • mutual_lock: Verify last_state.version == home_channel.state_version == escrow_channel.state_version AND next transition must be escrow_deposit
  • escrow_lock: Verify last_state.version == escrow_channel.state_version AND next transition must be escrow_withdraw or migrate
  • escrow_withdraw: Verify last_state.version == escrow_channel.state_version
  • migrate: Verify last_state.version == home_channel.state_version

func (*DBStore) ExecuteInTransaction

func (s *DBStore) ExecuteInTransaction(txFunc StoreTxHandler) error

func (*DBStore) Fail

func (s *DBStore) Fail(actionID int64, err string) error

func (*DBStore) FailNoRetry

func (s *DBStore) FailNoRetry(actionID int64, err string) error

func (*DBStore) GetActions

func (s *DBStore) GetActions(limit uint8, blockchainID uint64) ([]BlockchainAction, error)

func (*DBStore) GetActiveHomeChannel

func (s *DBStore) GetActiveHomeChannel(wallet, asset string) (*core.Channel, error)

GetActiveHomeChannel retrieves the active home channel for a user's wallet and asset. "Active" means the node has co-signed the channel definition (status Void or Open) — it does NOT guarantee the channel has been materialized onchain. Callers requiring onchain materialization (e.g., cross-chain escrow operations) must additionally check that Status == ChannelStatusOpen.

func (*DBStore) GetApp

func (s *DBStore) GetApp(appID string) (*app.AppInfoV1, error)

GetApp retrieves a single application by ID. Returns nil if not found.

func (*DBStore) GetAppCount

func (s *DBStore) GetAppCount(ownerWallet string) (uint64, error)

func (*DBStore) GetAppSession

func (s *DBStore) GetAppSession(sessionID string) (*app.AppSessionV1, error)

GetAppSession retrieves a specific session by ID.

func (*DBStore) GetAppSessionBalances

func (s *DBStore) GetAppSessionBalances(appSessionID string) (map[string]decimal.Decimal, error)

GetAppSessionBalances retrieves the total balances associated with a session.

func (*DBStore) GetAppSessionKeyOwner

func (s *DBStore) GetAppSessionKeyOwner(sessionKey, appSessionId, applicationId string) (string, error)

GetAppSessionKeyOwner returns the user_address that owns the given session key authorized for the specified app session ID or application ID. Only the latest-version, non-expired key with matching permissions is considered. A newer version always supersedes older ones.

Both appSessionId and applicationId are accepted because the app session row may not yet exist in the database when this is called (notably during app session creation). Passing applicationId directly avoids a chicken-and-egg subquery against the not-yet-inserted row; callers always know the application ID either from the signed request (create) or from the loaded app session record (post-create state updates).

func (*DBStore) GetAppSessions

func (s *DBStore) GetAppSessions(appSessionID *string, participant *string, status app.AppSessionStatus, pagination *core.PaginationParams) ([]app.AppSessionV1, core.PaginationMetadata, error)

GetAppSessions retrieves filtered sessions with pagination.

func (*DBStore) GetAppSessionsCountByLabels

func (s *DBStore) GetAppSessionsCountByLabels() ([]AppSessionCount, error)

GetAppSessionsCountByLabels returns current app session counts grouped by application and status.

func (*DBStore) GetApps

func (s *DBStore) GetApps(appID *string, ownerWallet *string, pagination *core.PaginationParams) ([]app.AppInfoV1, core.PaginationMetadata, error)

GetApps retrieves applications with optional filtering by app ID, owner wallet, and pagination.

func (*DBStore) GetChannelByID

func (s *DBStore) GetChannelByID(channelID string) (*core.Channel, error)

GetChannelByID retrieves a channel by its unique identifier.

func (*DBStore) GetChannelsCountByLabels

func (s *DBStore) GetChannelsCountByLabels() ([]ChannelCount, error)

GetChannelsCountByLabels returns current channel counts grouped by asset and status.

func (*DBStore) GetLastAppSessionKeyState

func (s *DBStore) GetLastAppSessionKeyState(wallet, sessionKey string) (*app.AppSessionKeyStateV1, error)

GetLastAppSessionKeyState retrieves the latest version of a specific session key for a user. A newer version always supersedes older ones, even if expired. Resolved via the pointer table; returns nil if no state exists.

func (*DBStore) GetLastAppSessionKeyStates

func (s *DBStore) GetLastAppSessionKeyStates(wallet string, sessionKey *string, includeInactive bool, limit, offset uint32) ([]app.AppSessionKeyStateV1, uint32, error)

GetLastAppSessionKeyStates retrieves the latest session key states for a user with optional filtering. Reads filter the current_session_key_states_v1 pointer table by (user_address, kind=app_session) and JOIN history on (user_address, session_key, version). Per-request DB work is bounded by the number of distinct session keys for the user, regardless of version churn in history. When includeInactive is false the same now is applied to both the count and the list query so pagination stays consistent across the two reads. Results are paginated; totalCount is the unpaginated total of matching session keys.

func (*DBStore) GetLastAppSessionKeyVersion

func (s *DBStore) GetLastAppSessionKeyVersion(wallet, sessionKey string) (uint64, error)

GetLastAppSessionKeyVersion returns the latest version of a session key state for a user. Reads from the pointer table; returns 0 if no state exists or the pointer is at its seeded value (LockSessionKeyState created the row but no submit has succeeded yet).

func (*DBStore) GetLastChannelSessionKeyStates

func (s *DBStore) GetLastChannelSessionKeyStates(wallet string, sessionKey *string, includeInactive bool, limit, offset uint32) ([]core.ChannelSessionKeyStateV1, uint32, error)

GetLastChannelSessionKeyStates retrieves the latest channel session key states for a user with optional filtering. Reads filter the current_session_key_states_v1 pointer table by (user_address, kind=channel) and JOIN history on (user_address, session_key, version). Per-request DB work is bounded by the number of distinct session keys for the user, regardless of version churn in history. When includeInactive is false the same now is applied to both the count and the list query so pagination stays consistent across the two reads. Results are paginated; totalCount is the unpaginated total of matching session keys.

func (*DBStore) GetLastChannelSessionKeyVersion

func (s *DBStore) GetLastChannelSessionKeyVersion(wallet, sessionKey string) (uint64, error)

GetLastChannelSessionKeyVersion returns the latest version of a channel session key state. Reads from the pointer table; returns 0 if no state exists or the pointer is at its seeded value (LockSessionKeyState created the row but no submit has succeeded yet).

func (*DBStore) GetLastStateByChannelID

func (s *DBStore) GetLastStateByChannelID(channelID string, signed bool) (*core.State, error)

GetLastStateByChannelID retrieves the most recent state for a given channel. Uses UNION ALL of two indexed queries instead of OR for better performance.

func (*DBStore) GetLastUserState

func (s *DBStore) GetLastUserState(wallet, asset string, signed bool) (*core.State, error)

GetLastUserState retrieves the most recent state for a user's asset.

func (*DBStore) GetLatestContractEventBlockNumber

func (s *DBStore) GetLatestContractEventBlockNumber(contractAddress string, blockchainID uint64) (uint64, error)

GetLatestContractEventBlockNumber returns the highest block number stored for a given contract.

func (*DBStore) GetLifetimeMetricLastTimestamp

func (s *DBStore) GetLifetimeMetricLastTimestamp(name string) (time.Time, error)

GetLifetimeMetricLastTimestamp returns the most recent last_timestamp among all metrics with the given name.

func (*DBStore) GetNodeBalance

func (s *DBStore) GetNodeBalance() ([]NodeBalance, error)

GetNodeBalance returns the on-chain liquidity per blockchain and asset.

func (*DBStore) GetNotClosedHomeChannel

func (s *DBStore) GetNotClosedHomeChannel(wallet, asset string) (*core.Channel, error)

GetNotClosedHomeChannel retrieves the home channel for a user's wallet and asset as long as it has not reached ChannelStatusClosed. This is broader than GetActiveHomeChannel (which stops at Open) and is intended for read paths that must remain functional after an off-chain Finalize, such as fetching channel data before submitting an on-chain close.

func (*DBStore) GetParticipantAllocations

func (s *DBStore) GetParticipantAllocations(appSessionID string) (map[string]map[string]decimal.Decimal, error)

GetParticipantAllocations retrieves specific asset allocations per participant. This will only return participants who have non-zero balances.

func (*DBStore) GetStateByChannelIDAndVersion

func (s *DBStore) GetStateByChannelIDAndVersion(channelID string, version uint64) (*core.State, error)

GetStateByChannelIDAndVersion retrieves a specific state version for a channel. Uses UNION ALL of two indexed queries instead of OR for better performance.

func (*DBStore) GetStateByID

func (s *DBStore) GetStateByID(stateID string) (*core.State, error)

GetStateByID retrieves a state by its deterministic ID.

func (*DBStore) GetTotalUserStaked

func (s *DBStore) GetTotalUserStaked(wallet string) (decimal.Decimal, error)

GetTotalUserStaked returns the total staked amount for a user across all blockchains.

func (*DBStore) GetTotalValueLocked

func (s *DBStore) GetTotalValueLocked() ([]TotalValueLocked, error)

func (*DBStore) GetUserActionCount

func (s *DBStore) GetUserActionCount(wallet string, gatedAction core.GatedAction, window time.Duration) (uint64, error)

GetUserActionCount returns the number of actions matching the given wallet and gated action within the specified time window (counting backwards from now).

func (*DBStore) GetUserActionCounts

func (s *DBStore) GetUserActionCounts(userWallet string, window time.Duration) (map[core.GatedAction]uint64, error)

func (*DBStore) GetUserBalanceSummary

func (s *DBStore) GetUserBalanceSummary() ([]UserBalanceSummary, error)

GetUserBalanceSummary returns off-chain liquidity metrics per blockchain and asset. Results include rows with home_blockchain_id = 0, representing balances not yet associated with a home blockchain — useful for surfacing funds that aren't attributed to any specific chain.

func (*DBStore) GetUserBalances

func (s *DBStore) GetUserBalances(wallet string) ([]core.BalanceEntry, error)

GetUserBalances retrieves the balances for a user's wallet.

func (*DBStore) GetUserChannels

func (s *DBStore) GetUserChannels(wallet string, status *core.ChannelStatus, asset *string, channelType *core.ChannelType, limit, offset uint32) ([]core.Channel, uint32, error)

GetUserChannels retrieves all channels for a user with optional status, asset, and type filters.

func (*DBStore) GetUserTransactions

func (s *DBStore) GetUserTransactions(accountID string, asset *string, txType *core.TransactionType, fromTime *uint64, toTime *uint64, paginate *core.PaginationParams) ([]core.Transaction, core.PaginationMetadata, error)

GetUserTransactions retrieves transaction history for a user with optional filters.

func (*DBStore) HasNonClosedHomeChannel

func (s *DBStore) HasNonClosedHomeChannel(wallet, asset string) (bool, error)

HasNonClosedHomeChannel returns true if any home channel for (wallet, asset) has a status other than Closed, meaning a channel lifecycle is still in progress.

func (*DBStore) HasSignedFinalize

func (s *DBStore) HasSignedFinalize(channelID string) (bool, error)

HasSignedFinalize reports whether a node-signed Finalize state exists for the given home channel. Used by event handlers to detect the post-Finalize lifecycle without loading the state row: the channels.status field can be temporarily overwritten by Challenged on a stale on-chain challenge, but the Finalize row persists here and is the authoritative marker. node_sig IS NOT NULL is checked explicitly so the contract holds even if a future path ever stores a Finalize row before the node signs.

func (*DBStore) IsContractEventPresent

func (s *DBStore) IsContractEventPresent(blockchainID, blockNumber uint64, txHash string, logIndex uint32) (bool, error)

IsContractEventPresent checks whether a specific contract event has already been stored.

func (*DBStore) LockSessionKeyState

func (s *DBStore) LockSessionKeyState(userAddress, sessionKey string, kind SessionKeyKind) (uint64, time.Time, error)

LockSessionKeyState seeds the pointer row for (userAddress, session_key, kind) if absent and locks the (session_key, kind) row for the surrounding transaction. Returns the latest stored version for the caller's row, or ErrSessionKeyNotAllowed if the key is bound to a different wallet for this kind.

The (session_key, kind) unique constraint guarantees there is at most one pointer row per (session_key, kind), so the SELECT ... FOR UPDATE that follows the no-op-on-conflict insert always converges on the same physical row regardless of who tried to seed first. A foreign wallet that races a legitimate owner ends up reading the legitimate owner back from the locked row and is rejected here, without parsing constraint-violation errors at write time.

SELECT ... FOR UPDATE is postgres-only; on sqlite the locking clause is skipped and the surrounding transaction provides the necessary ordering for the in-process test setup.

Seed-row permanence: the version=0 row written below is intentionally never deleted on failure paths (sig validation, version mismatch, cap exceeded, mid-tx errors). Once a wallet has staked a claim on (session_key, kind), no other wallet can take it for that kind — the seed is the ownership reservation, not a transient placeholder. CountSessionKeysForUser excludes version=0 rows so the per-user cap is unaffected, but the (session_key, kind) ownership bind is permanent by design.

When locked.Version > 0, the matching history row's expires_at is also returned so callers can distinguish a reactivation (prev inactive → submitted active) from a rotation/update (prev still active) and re-run the per-user cap on the reactivation path. When locked.Version == 0 there is no history yet, so a zero time.Time is returned.

func (*DBStore) LockUserState

func (s *DBStore) LockUserState(wallet, asset string) (decimal.Decimal, error)

LockUserState locks a user's balance row for update (postgres only, must be used within a transaction). Uses INSERT ... ON CONFLICT DO NOTHING to ensure the row exists, then SELECT ... FOR UPDATE to lock it. Returns the current balance or zero if the row was just inserted.

func (*DBStore) RecordAction

func (s *DBStore) RecordAction(wallet string, gatedAction core.GatedAction) error

RecordAction inserts a new action log entry for a user.

func (*DBStore) RecordAttempt

func (s *DBStore) RecordAttempt(actionID int64, err string) error

func (*DBStore) RecordLedgerEntry

func (s *DBStore) RecordLedgerEntry(userWallet, accountID, asset string, amount decimal.Decimal) error

RecordLedgerEntry logs a movement of funds within the internal ledger.

func (*DBStore) RecordTransaction

func (s *DBStore) RecordTransaction(tx core.Transaction, applicationID string) error

RecordTransaction creates a transaction record linking state transitions. applicationID is the client-declared origin tag; empty string → NULL column.

func (*DBStore) RefreshUserEnforcedBalance

func (s *DBStore) RefreshUserEnforcedBalance(wallet, asset string) error

RefreshUserEnforcedBalance recomputes the enforced balance from the user's open home channel on-chain state.

func (*DBStore) ScheduleChallenge

func (s *DBStore) ScheduleChallenge(stateID string, blockchainID uint64) error

ScheduleChallenge queues a blockchain action to challenge a channel on its home blockchain using the provided state. The worker submits the state via challengeChannel(...) with a node-produced challenger signature.

func (*DBStore) ScheduleCheckpoint

func (s *DBStore) ScheduleCheckpoint(stateID string, blockchainID uint64) error

ScheduleCheckpoint queues a blockchain action to checkpoint a state on home blockchain.

func (*DBStore) ScheduleFinalizeEscrowDeposit

func (s *DBStore) ScheduleFinalizeEscrowDeposit(stateID string, blockchainID uint64) error

ScheduleFinalizeEscrowDeposit schedules a finalize for an escrow deposit operation on non-home blockchain.

func (*DBStore) ScheduleFinalizeEscrowWithdrawal

func (s *DBStore) ScheduleFinalizeEscrowWithdrawal(stateID string, blockchainID uint64) error

ScheduleFinalizeEscrowWithdrawal schedules a finalize for an escrow withdrawal operation on non-home blockchain.

func (*DBStore) ScheduleInitiateEscrowDeposit

func (s *DBStore) ScheduleInitiateEscrowDeposit(stateID string, blockchainID uint64) error

ScheduleInitiateEscrowDeposit queues a blockchain action to initiate escrow deposit on home blockchain.

func (*DBStore) ScheduleInitiateEscrowWithdrawal

func (s *DBStore) ScheduleInitiateEscrowWithdrawal(stateID string, blockchainID uint64) error

ScheduleInitiateEscrowWithdrawal queues a blockchain action to initiate withdrawal on non-home blockchain.

func (*DBStore) SetNodeBalance

func (s *DBStore) SetNodeBalance(blockchainID uint64, asset string, value decimal.Decimal) error

SetNodeBalance upserts the on-chain liquidity for a given blockchain and asset.

func (*DBStore) StoreAppSessionKeyState

func (s *DBStore) StoreAppSessionKeyState(state app.AppSessionKeyStateV1) error

StoreAppSessionKeyState stores a new session key state version.

func (*DBStore) StoreChannelSessionKeyState

func (s *DBStore) StoreChannelSessionKeyState(state core.ChannelSessionKeyStateV1) error

StoreChannelSessionKeyState stores a new channel session key state version.

func (*DBStore) StoreContractEvent

func (s *DBStore) StoreContractEvent(ev core.BlockchainEvent) error

StoreContractEvent stores a blockchain event to the database. This function matches the signature required by pkg/blockchain/evm.StoreContractEvent.

func (*DBStore) StoreUserState

func (s *DBStore) StoreUserState(state core.State, applicationID string) error

StoreUserState persists a new user state to the database. applicationID is the client-declared origin tag; empty string → NULL column.

func (*DBStore) SumNetTransitionAmountAfterVersion

func (s *DBStore) SumNetTransitionAmountAfterVersion(channelID string, minVersion uint64) (decimal.Decimal, error)

SumNetTransitionAmountAfterVersion returns the net effect on the user's home-channel balance of transitions stored against channelID strictly above minVersion. Receiver credits (TransferReceive, Release) contribute positively; sender debits (TransferSend, Commit) contribute negatively. Other transition kinds (HomeDeposit, HomeWithdrawal, escrow ops, migrate, finalize, acknowledgement) are excluded because they either require onchain backing the chain didn't enforce at this closure or belong to a different ledger.

Used to compute the ChallengeRescue amount when a challenged channel is closed: onchain payout reflects the closure version, anything strictly above didn't enforce, and the net amount the user is still owed by the node is the receives minus the sends in that interval. Signed (Open-time) and unsigned (during-challenge) rows both contribute — both are real value the state-advancer validated at submit time. The caller clamps the result at zero before issuing the rescue.

No epoch filter: a channel's in-channel rows live at a single epoch; detached post-Finalize rows have home_channel_id NULL and are already excluded by the channel_id predicate.

func (*DBStore) UpdateAppSession

func (s *DBStore) UpdateAppSession(session app.AppSessionV1) error

UpdateAppSession updates existing session data with optimistic locking.

func (*DBStore) UpdateChannel

func (s *DBStore) UpdateChannel(channel core.Channel) error

UpdateChannel persists changes to a channel's metadata (status, version, etc).

func (*DBStore) UpdateStateSigsIfMissing

func (s *DBStore) UpdateStateSigsIfMissing(channelID string, version uint64, userSig, nodeSig string) error

UpdateStateSigsIfMissing backfills the user and/or node signatures for a stored state when the corresponding column is currently NULL. Used by the on-chain event reactor to repair the local record once a state has been enforced on chain. Either signature may be empty, in which case that side is skipped. The IS NULL guard keeps the call idempotent on event replay and prevents overwriting a signature already populated by the user-facing RPC path.

func (*DBStore) UpdateUserStaked

func (s *DBStore) UpdateUserStaked(wallet string, blockchainID uint64, amount decimal.Decimal) error

UpdateUserStaked upserts the staked amount for a user on a specific blockchain.

func (*DBStore) ValidateChannelSessionKeyForAsset

func (s *DBStore) ValidateChannelSessionKeyForAsset(wallet, sessionKey, asset, metadataHash string) (bool, error)

ValidateChannelSessionKeyForAsset checks in a single query that: - a session key state exists for the (wallet, sessionKey) pair, - it is the latest version, - it is not expired, - the asset is in the allowed list, - the metadata hash matches.

type DatabaseConfig

type DatabaseConfig struct {
	URL      string `env:"NITRONODE_DATABASE_URL" env-default:""`
	Name     string `env:"NITRONODE_DATABASE_NAME" env-default:""`
	Schema   string `env:"NITRONODE_DATABASE_SCHEMA" env-default:""`
	Driver   string `env:"NITRONODE_DATABASE_DRIVER" env-default:"postgres"`
	Username string `env:"NITRONODE_DATABASE_USERNAME"  env-default:"postgres"`
	Password string `env:"NITRONODE_DATABASE_PASSWORD" env-default:"your-super-secret-and-long-postgres-password"`
	Host     string `env:"NITRONODE_DATABASE_HOST" env-default:"localhost"`
	Port     string `env:"NITRONODE_DATABASE_PORT" env-default:"5432"`
	SSLMode  string `env:"NITRONODE_DATABASE_SSLMODE" env-default:"require"`
	Retries  int    `env:"NITRONODE_DATABASE_RETRIES" env-default:"5"`

	// Connection pool settings
	MaxOpenConns    int `env:"NITRONODE_DATABASE_MAX_OPEN_CONNS" env-default:"100"`
	MaxIdleConns    int `env:"NITRONODE_DATABASE_MAX_IDLE_CONNS" env-default:"25"`
	ConnMaxLifetime int `env:"NITRONODE_DATABASE_CONN_MAX_LIFETIME_SEC" env-default:"300"`
	ConnMaxIdleTime int `env:"NITRONODE_DATABASE_CONN_MAX_IDLE_TIME_SEC" env-default:"60"`
}

In order to connect to Postgresql you need to fill out all the fields.

To connect to sqlite, you just need to specify "sqlite" driver. By default it will use in-memory database. You can provide NITRONODE_DATABASE_NAME to use the file.

For Postgresql, NITRONODE_DATABASE_URL takes precedence: when set, it is used verbatim and the individual Username/Password/Host/Port/Name/SSLMode fields are ignored.

type DatabaseStore

type DatabaseStore interface {
	// ExecuteInTransaction runs the provided handler within a database transaction.
	// If the handler returns an error, the transaction is rolled back.
	// If the handler completes successfully, the transaction is committed.
	ExecuteInTransaction(handler StoreTxHandler) error

	// GetUserBalances retrieves the balances for a user's wallet.
	GetUserBalances(wallet string) ([]core.BalanceEntry, error)

	// LockUserState locks a user's balance row for update (postgres only, must be used within a transaction).
	// Uses INSERT ... ON CONFLICT DO NOTHING to ensure the row exists, then SELECT ... FOR UPDATE to lock it.
	// Returns the current balance or zero if the row was just inserted.
	LockUserState(wallet, asset string) (decimal.Decimal, error)

	// GetUserTransactions retrieves transaction history for a user with optional filters.
	GetUserTransactions(wallet string, asset *string, txType *core.TransactionType, fromTime *uint64, toTime *uint64, paginate *core.PaginationParams) ([]core.Transaction, core.PaginationMetadata, error)

	// RecordTransaction creates a transaction record linking state transitions.
	// applicationID is the self-declared origin tag of the client that caused
	// the transaction (see rpc.ApplicationIDQueryParam); empty string means no
	// app_id was supplied and the column is persisted as NULL.
	RecordTransaction(tx core.Transaction, applicationID string) error

	// CreateChannel creates a new channel entity in the database.
	CreateChannel(channel core.Channel) error

	// GetChannelByID retrieves a channel by its unique identifier.
	GetChannelByID(channelID string) (*core.Channel, error)

	// GetActiveHomeChannel retrieves the active home channel for a user's wallet and asset.
	// "Active" includes both Void (DB-only) and Open (materialized onchain).
	GetActiveHomeChannel(wallet, asset string) (*core.Channel, error)

	// GetNotClosedHomeChannel retrieves the home channel for a user's wallet and asset
	// regardless of status, as long as it has not reached ChannelStatusClosed. Intended
	// for read paths (e.g. GetHomeChannel RPC) that must remain functional after an
	// off-chain Finalize flips the channel to Closing.
	GetNotClosedHomeChannel(wallet, asset string) (*core.Channel, error)

	// CheckActiveChannel verifies if a user has an active home channel for the given asset
	// and returns its approved signature validators and current status. A nil status means
	// no active channel exists. "Active" includes Void (DB-only, awaiting onchain confirmation)
	// and Open (materialized onchain); callers needing onchain materialization must additionally
	// require Status == core.ChannelStatusOpen.
	CheckActiveChannel(wallet, asset string) (string, *core.ChannelStatus, error)

	// HasNonClosedHomeChannel returns true if any home channel for (wallet, asset) exists
	// with a status other than Closed, indicating an in-progress channel lifecycle.
	HasNonClosedHomeChannel(wallet, asset string) (bool, error)

	// UpdateChannel persists changes to a channel's metadata (status, version, etc).
	UpdateChannel(channel core.Channel) error

	// GetUserChannels retrieves all channels for a user with optional status, asset, and type filters.
	GetUserChannels(wallet string, status *core.ChannelStatus, asset *string, channelType *core.ChannelType, limit, offset uint32) ([]core.Channel, uint32, error)

	// GetLastStateByChannelID retrieves the most recent state for a given channel.
	// If signed is true, only returns states with both user and node signatures.
	GetLastStateByChannelID(channelID string, signed bool) (*core.State, error)

	// GetStateByChannelIDAndVersion retrieves a specific state version for a channel.
	// Returns nil if the state with the specified version does not exist.
	GetStateByChannelIDAndVersion(channelID string, version uint64) (*core.State, error)

	// GetLastUserState retrieves the most recent state for a user's asset.
	GetLastUserState(wallet, asset string, signed bool) (*core.State, error)

	// StoreUserState persists a new user state to the database.
	// applicationID is the self-declared origin tag of the client that caused
	// the transition (see rpc.ApplicationIDQueryParam); empty string means no
	// app_id was supplied and the column is persisted as NULL.
	StoreUserState(state core.State, applicationID string) error

	// EnsureNoOngoingStateTransitions validates that no conflicting blockchain operations are pending.
	EnsureNoOngoingStateTransitions(wallet, asset string) error

	// EnsureNoOngoingEscrowOperation validates that the user has no in-flight escrow
	// operation (escrow_lock, mutual_lock, or unfinalized escrow_deposit/escrow_withdraw)
	// that would prevent issuing a receiver-side state.
	EnsureNoOngoingEscrowOperation(wallet, asset string) error

	// UpdateStateSigsIfMissing backfills the user and/or node signatures for a stored
	// state when the corresponding column is currently NULL. Either signature may be
	// empty to skip that side.
	UpdateStateSigsIfMissing(channelID string, version uint64, userSig, nodeSig string) error

	// HasSignedFinalize reports whether a node-signed Finalize state exists for the given
	// home channel. Used by event handlers to detect the post-Finalize lifecycle when the
	// channel status field has been temporarily overwritten by an on-chain challenge.
	HasSignedFinalize(channelID string) (bool, error)

	// SumNetTransitionAmountAfterVersion returns the net effect on the user's
	// home-channel balance of transitions stored against channelID strictly above
	// minVersion. Receiver credits (TransferReceive, Release) contribute positively;
	// sender debits (TransferSend, Commit) contribute negatively. Other transition
	// kinds are excluded. Used to compute the ChallengeRescue amount when a
	// challenged channel is closed.
	SumNetTransitionAmountAfterVersion(channelID string, minVersion uint64) (decimal.Decimal, error)

	// ScheduleInitiateEscrowWithdrawal queues a blockchain action to initiate withdrawal.
	// This queues the state to be submitted on-chain to initiate an escrow withdrawal.
	ScheduleInitiateEscrowWithdrawal(stateID string, chainID uint64) error

	// ScheduleCheckpoint schedules a checkpoint operation for a home channel state.
	// This queues the state to be submitted on-chain to update the channel's on-chain state.
	ScheduleCheckpoint(stateID string, chainID uint64) error

	// ScheduleChallenge schedules a challengeChannel(...) submission on the channel's home
	// blockchain using the provided state and a node-produced challenger signature.
	ScheduleChallenge(stateID string, chainID uint64) error

	// ScheduleFinalizeEscrowDeposit schedules a checkpoint for an escrow deposit operation.
	// This queues the state to be submitted on-chain to finalize an escrow deposit.
	ScheduleFinalizeEscrowDeposit(stateID string, chainID uint64) error

	// ScheduleFinalizeEscrowWithdrawal schedules a checkpoint for an escrow withdrawal operation.
	// This queues the state to be submitted on-chain to finalize an escrow withdrawal.
	ScheduleFinalizeEscrowWithdrawal(stateID string, chainID uint64) error

	// ScheduleInitiateEscrowDeposit schedules a checkpoint for an escrow deposit operation.
	// This queues the state to be submitted on-chain for an escrow deposit on home chain.
	ScheduleInitiateEscrowDeposit(stateID string, chainID uint64) error

	// Fail marks a blockchain action as failed and increments the retry counter.
	Fail(actionID int64, err string) error

	// FailNoRetry marks a blockchain action as failed without incrementing the retry counter.
	FailNoRetry(actionID int64, err string) error

	// RecordAttempt records a failed attempt for a blockchain action and increments the retry counter.
	// The action remains in pending status.
	RecordAttempt(actionID int64, err string) error

	// Complete marks a blockchain action as completed with the given transaction hash.
	Complete(actionID int64, txHash string) error

	// GetActions retrieves pending blockchain actions, optionally limited by count.
	GetActions(limit uint8, chainID uint64) ([]BlockchainAction, error)

	// GetStateByID retrieves a state by its deterministic ID.
	GetStateByID(stateID string) (*core.State, error)

	// CreateApp registers a new application. Returns an error if the app ID already exists.
	CreateApp(entry app.AppV1) error

	// GetApp retrieves a single application by ID. Returns nil if not found.
	GetApp(appID string) (*app.AppInfoV1, error)

	// GetApps retrieves applications with optional filtering by app ID, owner wallet, and pagination.
	GetApps(appID *string, ownerWallet *string, pagination *core.PaginationParams) ([]app.AppInfoV1, core.PaginationMetadata, error)

	// GetAppCount returns the total number of applications owned by a specific wallet.
	GetAppCount(ownerWallet string) (uint64, error)

	// CreateAppSession initializes a new application session.
	CreateAppSession(session app.AppSessionV1) error

	// GetAppSession retrieves a specific session by ID.
	GetAppSession(sessionID string) (*app.AppSessionV1, error)

	// GetAppSessions retrieves filtered sessions with pagination.
	GetAppSessions(appSessionID *string, participant *string, status app.AppSessionStatus, pagination *core.PaginationParams) ([]app.AppSessionV1, core.PaginationMetadata, error)

	// UpdateAppSession updates existing session data.
	UpdateAppSession(session app.AppSessionV1) error

	// GetAppSessionBalances retrieves the total balances associated with a session.
	GetAppSessionBalances(sessionID string) (map[string]decimal.Decimal, error)

	// GetParticipantAllocations retrieves specific asset allocations per participant.
	GetParticipantAllocations(sessionID string) (map[string]map[string]decimal.Decimal, error)

	// RecordLedgerEntry logs a movement of funds within the internal ledger.
	RecordLedgerEntry(userWallet, accountID, asset string, amount decimal.Decimal) error

	// LockSessionKeyState seeds the pointer row for (user, session_key, kind) if absent and
	// locks the (session_key, kind) row for the surrounding transaction. Returns the latest
	// stored version for the caller's row and the latestExpiresAt of that version (zero time
	// when the latest version is 0, i.e. no history row exists yet). Returns
	// ErrSessionKeyNotAllowed if the key is bound to a different wallet for this kind.
	// The latestExpiresAt return lets submit handlers distinguish a reactivation
	// (prev inactive → submitted active) from a rotation (prev already active) so the
	// per-user cap can be re-checked when a revoked slot is brought back.
	LockSessionKeyState(userAddress, sessionKey string, kind SessionKeyKind) (latestVersion uint64, latestExpiresAt time.Time, err error)

	// CountSessionKeysForUser returns the number of distinct session keys recorded for the
	// wallet across both kinds. Used to enforce the per-user cap at submit time.
	CountSessionKeysForUser(userAddress string) (uint32, error)

	// StoreAppSessionKeyState stores or updates a session key state.
	StoreAppSessionKeyState(state app.AppSessionKeyStateV1) error

	GetAppSessionKeyOwner(sessionKey, appSessionId, applicationId string) (string, error)

	// SessionKeyStateExists returns the latest version of a non-expired session key state for a user.
	// Returns 0 if no state exists.
	GetLastAppSessionKeyVersion(wallet, sessionKey string) (uint64, error)

	// GetLatestSessionKeyState retrieves the latest version of a specific session key for a user.
	// Returns nil if no state exists.
	GetLastAppSessionKeyState(wallet, sessionKey string) (*app.AppSessionKeyStateV1, error)

	// GetLastAppSessionKeyStates retrieves the latest session key states for a user with optional
	// filtering. When includeInactive is false, only states whose expires_at is in the future are
	// returned; when true, all latest states are returned regardless of expiry. Results are
	// paginated; totalCount is the unpaginated total matching the filter.
	GetLastAppSessionKeyStates(wallet string, sessionKey *string, includeInactive bool, limit, offset uint32) ([]app.AppSessionKeyStateV1, uint32, error)

	// StoreChannelSessionKeyState stores or updates a channel session key state.
	StoreChannelSessionKeyState(state core.ChannelSessionKeyStateV1) error

	// GetLastChannelSessionKeyVersion returns the latest version for a (wallet, sessionKey) pair.
	// Returns 0 if no state exists.
	GetLastChannelSessionKeyVersion(wallet, sessionKey string) (uint64, error)

	// GetLastChannelSessionKeyStates retrieves the latest channel session key states for a user,
	// optionally filtered by session key. When includeInactive is false, only states whose
	// expires_at is in the future are returned; when true, all latest states are returned
	// regardless of expiry. Results are paginated; totalCount is the unpaginated total matching
	// the filter.
	GetLastChannelSessionKeyStates(wallet string, sessionKey *string, includeInactive bool, limit, offset uint32) ([]core.ChannelSessionKeyStateV1, uint32, error)

	// ValidateChannelSessionKeyForAsset checks that a valid, non-expired session key state
	// exists at its latest version for the (wallet, sessionKey) pair, includes the given asset,
	// and matches the metadata hash.
	ValidateChannelSessionKeyForAsset(wallet, sessionKey, asset, metadataHash string) (bool, error)

	// CountActiveUsers returns distinct user counts per asset within the given window.
	CountActiveUsers(window time.Duration) ([]ActiveCountByLabel, error)

	// CountActiveAppSessions returns app session counts per application within the given window.
	CountActiveAppSessions(window time.Duration) ([]ActiveCountByLabel, error)

	// GetLifetimeMetricLastTimestamp returns the most recent last_timestamp among all metrics with the given name.
	GetLifetimeMetricLastTimestamp(name string) (time.Time, error)

	// GetAppSessionsCountByLabels computes app session count deltas, upserts as lifespan metrics, and returns updated totals.
	GetAppSessionsCountByLabels() ([]AppSessionCount, error)

	// GetChannelsCountByLabels computes channel count deltas, upserts as lifespan metrics, and returns updated totals.
	GetChannelsCountByLabels() ([]ChannelCount, error)

	// GetTotalValueLocked computes TVL deltas by domain (channels, app_sessions) and asset, upserts as lifespan metrics, and returns updated totals.
	GetTotalValueLocked() ([]TotalValueLocked, error)

	// SetNodeBalance upserts the on-chain liquidity for a given blockchain and asset.
	SetNodeBalance(blockchainID uint64, asset string, value decimal.Decimal) error

	// RefreshUserEnforcedBalance recomputes the locked balance from the user's open home channel on-chain state.
	RefreshUserEnforcedBalance(wallet, asset string) error

	// GetNodeBalance returns the on-chain liquidity per blockchain and asset.
	GetNodeBalance() ([]NodeBalance, error)

	// GetUserBalanceSummary returns the off-chain liquidity requirement per blockchain and asset.
	GetUserBalanceSummary() ([]UserBalanceSummary, error)

	// UpdateUserStaked upserts the staked amount for a user on a specific blockchain.
	UpdateUserStaked(wallet string, blockchainID uint64, amount decimal.Decimal) error

	// GetTotalUserStaked returns the total staked amount for a user across all blockchains.
	GetTotalUserStaked(wallet string) (decimal.Decimal, error)

	// RecordAction inserts a new action log entry for a user.
	RecordAction(wallet string, gatedAction core.GatedAction) error

	// GetUserActionCount returns the number of actions matching the given wallet, method, and path
	// within the specified time window.
	GetUserActionCount(wallet string, gatedAction core.GatedAction, window time.Duration) (uint64, error)

	// GetUserActionCounts returns a map of gated actions to their respective counts for a user within the specified time window.
	GetUserActionCounts(userWallet string, window time.Duration) (map[core.GatedAction]uint64, error)

	// StoreContractEvent stores a blockchain event to prevent duplicate processing.
	StoreContractEvent(ev core.BlockchainEvent) error

	// GetLatestContractEventBlockNumber returns the highest block number for a given contract.
	GetLatestContractEventBlockNumber(contractAddress string, blockchainID uint64) (lastBlock uint64, err error)

	// IsContractEventPresent checks if a specific contract event has already been stored.
	IsContractEventPresent(blockchainID, blockNumber uint64, txHash string, logIndex uint32) (isPresent bool, err error)
}

DatabaseStore defines the unified persistence layer.

func NewDBStore

func NewDBStore(db *gorm.DB) DatabaseStore

type LifespanMetric

type LifespanMetric struct {
	ID            string          `gorm:"column:id;primaryKey;size:66"`
	Name          string          `gorm:"column:name;not null"`
	Labels        datatypes.JSON  `gorm:"column:labels;type:text"`
	Value         decimal.Decimal `gorm:"column:value;type:varchar(78);not null"`
	LastTimestamp time.Time       `gorm:"column:last_timestamp;not null"`
	UpdatedAt     time.Time
}

func (LifespanMetric) TableName

func (LifespanMetric) TableName() string

type ListOptions

type ListOptions struct {
	Offset uint32    `json:"offset,omitempty"`
	Limit  uint32    `json:"limit,omitempty"`
	Sort   *SortType `json:"sort,omitempty"` // Optional sort type (asc/desc)
}

type NodeBalance

type NodeBalance struct {
	BlockchainID string          `gorm:"column:blockchain_id"`
	Asset        string          `gorm:"column:asset"`
	Value        decimal.Decimal `gorm:"column:value"`
	LastUpdated  time.Time       `gorm:"column:last_updated"`
}

NodeBalance holds on-chain liquidity for a given blockchain and asset.

type QueryDurationObserver

type QueryDurationObserver interface {
	ObserveDBQueryDuration(queryKind string, duration time.Duration)
}

QueryDurationObserver records the time a single gorm DB operation took. Implemented by the runtime metric exporter to avoid an import cycle (the metrics package depends on pkg/{app,core,rpc}, store/database does not).

type SessionKeyKind

type SessionKeyKind uint8

SessionKeyKind discriminates the two session-key flavors stored in current_session_key_states_v1. Stored as SMALLINT in the DB.

const (
	SessionKeyKindChannel    SessionKeyKind = 1
	SessionKeyKindAppSession SessionKeyKind = 2
)

type SortType

type SortType string
const (
	SortTypeAscending  SortType = "asc"
	SortTypeDescending SortType = "desc"
)

func (SortType) ToString

func (s SortType) ToString() string

type State

type State struct {
	// ID is a 64-character deterministic hash
	ID         string `gorm:"column:id;primaryKey;size:64"`
	Asset      string `gorm:"column:asset;not null"`
	UserWallet string `gorm:"column:user_wallet;not null"`
	Epoch      uint64 `gorm:"column:epoch;not null"`
	Version    uint64 `gorm:"column:version;not null"`

	// Transition
	TransitionType      uint8           `gorm:"column:transition_type;not null"`
	TransitionTxID      string          `gorm:"column:transition_tx_id;size:66;not null"`
	TransitionAccountID string          `gorm:"column:transition_account_id;size:66;not null"`
	TransitionAmount    decimal.Decimal `gorm:"column:transition_amount;type:varchar(78);not null"`

	// Optional channel references
	HomeChannelID   *string `gorm:"column:home_channel_id"`
	EscrowChannelID *string `gorm:"column:escrow_channel_id"`

	// Home Channel balances and flows
	// Using decimal.Decimal for int256 values and int64 for flow values
	HomeUserBalance decimal.Decimal `gorm:"column:home_user_balance;type:varchar(78)"`
	HomeUserNetFlow decimal.Decimal `gorm:"column:home_user_net_flow;default:0"`
	HomeNodeBalance decimal.Decimal `gorm:"column:home_node_balance;type:varchar(78)"`
	HomeNodeNetFlow decimal.Decimal `gorm:"column:home_node_net_flow;default:0"`

	// Escrow Channel balances and flows
	EscrowUserBalance decimal.Decimal `gorm:"column:escrow_user_balance;type:varchar(78)"`
	EscrowUserNetFlow decimal.Decimal `gorm:"column:escrow_user_net_flow;default:0"`
	EscrowNodeBalance decimal.Decimal `gorm:"column:escrow_node_balance;type:varchar(78)"`
	EscrowNodeNetFlow decimal.Decimal `gorm:"column:escrow_node_net_flow;default:0"`

	UserSig *string `gorm:"column:user_sig;type:text"`
	NodeSig *string `gorm:"column:node_sig;type:text"`

	// ApplicationID is the self-declared origin tag of the client that caused
	// this state transition (see rpc.ApplicationIDQueryParam). Advisory only.
	ApplicationID *string `gorm:"column:application_id;size:66;index:idx_channel_states_app_id"`

	// Read-only fields populated from JOINs with channels table
	HomeBlockchainID   *uint64 `gorm:"->;column:home_blockchain_id"`
	HomeTokenAddress   *string `gorm:"->;column:home_token_address"`
	EscrowBlockchainID *uint64 `gorm:"->;column:escrow_blockchain_id"`
	EscrowTokenAddress *string `gorm:"->;column:escrow_token_address"`

	CreatedAt time.Time
}

State represents an immutable state in the system ID is deterministic: Hash(UserWallet, Asset, CycleIndex, Version)

func (State) TableName

func (State) TableName() string

TableName specifies the table name for the State model

type StoreTxHandler

type StoreTxHandler func(DatabaseStore) error

StoreTxHandler is a function that executes Store operations within a transaction.

type TotalValueLocked

type TotalValueLocked struct {
	Asset       string          `gorm:"column:asset"`
	Domain      string          `gorm:"column:domain"`
	Value       decimal.Decimal `gorm:"column:value"`
	LastUpdated time.Time       `gorm:"column:last_updated"`
}

TotalValueLocked holds the total value locked for a given asset, along with the last update timestamp.

type Transaction

type Transaction struct {
	// ID is a 64-character deterministic hash
	ID                 string               `gorm:"column:id;primaryKey;size:64"`
	Type               core.TransactionType `gorm:"column:tx_type;not null;index:idx_type;index:idx_from_to_account"`
	AssetSymbol        string               `gorm:"column:asset_symbol;not null"`
	FromAccount        string               `gorm:"column:from_account;not null;index:idx_from_account;index:idx_from_to_account"`
	ToAccount          string               `gorm:"column:to_account;not null;index:idx_to_account;index:idx_from_to_account"`
	SenderNewStateID   *string              `gorm:"column:sender_new_state_id;size:64"`
	ReceiverNewStateID *string              `gorm:"column:receiver_new_state_id;size:64"`
	Amount             decimal.Decimal      `gorm:"column:amount;type:decimal(38,18);not null"`
	CreatedAt          time.Time

	// ApplicationID is the self-declared origin tag of the client that caused
	// this transaction (see rpc.ApplicationIDQueryParam). Advisory only.
	ApplicationID *string `gorm:"column:application_id;size:66;index:idx_transactions_app_id"`
}

Transaction represents an immutable transaction in the system ID is deterministic based on transaction initiation: 1) Initiated by User: Hash(ToAccount, SenderNewStateID) 2) Initiated by Node: Hash(FromAccount, ReceiverNewStateID)

func (Transaction) TableName

func (Transaction) TableName() string

type UserBalance

type UserBalance struct {
	UserWallet       string          `gorm:"column:user_wallet;primaryKey;size:42"`
	Asset            string          `gorm:"column:asset;primaryKey;size:20"`
	Balance          decimal.Decimal `gorm:"column:balance;type:varchar(78);not null"`
	Enforced         decimal.Decimal `gorm:"column:enforced;type:varchar(78);not null;default:0"`
	HomeBlockchainID uint64          `gorm:"column:home_blockchain_id;not null;default:0"`
	CreatedAt        time.Time       `gorm:"column:created_at"`
	UpdatedAt        time.Time       `gorm:"column:updated_at"`
}

UserBalance represents aggregated user balance for an asset

func (UserBalance) TableName

func (UserBalance) TableName() string

TableName specifies the table name for the UserBalance model

type UserBalanceSummary

type UserBalanceSummary struct {
	BlockchainID string          `gorm:"column:home_blockchain_id"`
	Asset        string          `gorm:"column:asset"`
	Total        decimal.Decimal `gorm:"column:total"`
	Underfunded  decimal.Decimal `gorm:"column:underfunded"`
	Releasable   decimal.Decimal `gorm:"column:releasable"`
}

UserBalanceSummary holds off-chain liquidity metrics for a given blockchain and asset. BlockchainID is kept as a string to match the Prometheus exporter's label type, avoiding conversions at the call site.

type UserStakedV1

type UserStakedV1 struct {
	UserWallet   string          `gorm:"column:user_wallet;primaryKey;not null"`
	BlockchainID uint64          `gorm:"column:blockchain_id;primaryKey;not null"`
	Amount       decimal.Decimal `gorm:"column:amount;type:varchar(78);not null"`
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

func (UserStakedV1) TableName

func (UserStakedV1) TableName() string

Jump to

Keyboard shortcuts

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