
1. Introduction
Welcome to the official documentation for the GRIDNET OS State-Less Channels sub-system and its associated Off-The-Chain Payment API. This document provides UI dApp developers with the necessary understanding and technical guidance to leverage this powerful mechanism for enabling fast, cost-effective, and scalable value transfer and incentivization within their decentralized applications.
1.1. Purpose: The Need for Off-Chain Solutions in GRIDNET OS
GRIDNET OS provides a robust platform for executing decentralized logic and managing state through its Decentralized State Machine (DSM). However, like all blockchain-based systems, direct on-chain transactions have inherent limitations that can hinder certain types of applications:
-
Scalability:
The number of transactions per second (TPS) that can be processed directly on the main chain is finite. High-frequency operations can quickly saturate the network’s capacity.
-
Cost:
Every transaction committed to the DSM consumes resources (ERG) for processing and storage. While designed to be efficient, on-chain operations can become prohibitively expensive for very small or frequent value exchanges (micropayments).
-
Speed & Latency:
On-chain transactions require consensus and block finality, introducing latency. Real-time interactions demanding immediate confirmation or reward cannot solely rely on the main chain’s settlement time.
To overcome these challenges and unlock a wider range of possibilities for UI dApps, GRIDNET OS implements a sophisticated off-chain solution: the State-Less Channels system. This system is crucial for applications involving:
-
Micropayments:
Enabling tiny value transfers for services, content access, or rewards where on-chain fees would be disproportionately high.
-
Incentivized Data Exchange:
Efficiently rewarding peers for relaying data packets in real-time, essential for decentralized communication, storage, and services like the GRIDNET OS Web Proxy or WebRTC Swarms.
-
Real-time Rewards:
Instantly rewarding user actions within games (like the Snake dApp) or other interactive applications without waiting for block confirmations.
1.2. Overview of the State-Less Channels System
The GRIDNET OS State-Less Channels system facilitates secure value transfer between parties without requiring every individual transfer to be recorded directly on the main blockchain (the DSM).
-
Analogy to Lightning Network:
The goal is similar to layer-2 scaling solutions like Bitcoin’s Lightning Network – enabling faster, cheaper transactions off-chain. However, the underlying mechanics in GRIDNET OS are distinct. State-Less Channels do not rely on complex routed payment channels requiring intermediate node participation in the same way.
-
Core Idea: Secure Off-Chain Value Transfer Based on Pre-Funded Pools:
The system operates on the principle of
Multi-Dimensional Token Pools (M-DTPs)
. These pools represent a store of value, initially funded via an on-chain sacrificial transaction and registered on the DSM. Value is then exchanged off-chain by securely revealing pre-images (hashes or “tokens”) derived from secrets associated with the pool’s dimensions (banks). Only the initial pool setup and the final “cashing out” of accumulated off-chain tokens require direct interaction with the DSM, significantly reducing on-chain load. The “State-Less” nature refers to the fact that complex channel state negotiation and management between peers are minimized compared to traditional state channels.
1.3. Key Benefits
1.4. GRIDNET OS’ Decentralized Incentivized SOA Paradigm
1.4.1. Introduction to Service-Oriented Architecture in GRIDNET OS
GRIDNET OS implements a unique approach to Service-Oriented Architecture (SOA) that fundamentally reimagines how distributed services can operate in a decentralized environment. Unlike traditional SOA implementations that rely on centralized service registries, orchestration, and payment systems, GRIDNET OS’ architecture distributes all aspects of service discovery, execution, and compensation across its peer-to-peer network.
Service-Oriented Architecture refers to a design pattern where applications are built from loosely coupled, independently deployable services that communicate through standardized interfaces. GRIDNET OS extends this concept by adding three critical dimensions:
-
Full Decentralization
Services operate without central authorities or single points of failure -
Built-in Incentivization
Automatic, fine-grained compensation for service providers -
Sybil-Resistant Economics
Game-theoretical protection against network manipulation
1.4.2. Core Components of the Decentralized SOA
The GRIDNET OS Decentralized Incentivized SOA consists of several interconnected components:
Service Providers
Independent nodes that offer computational resources, storage capacity, bandwidth, or specialized functionality to the network. These can include:
- Data relay nodes
- Content delivery services
- Computational services
- Storage providers
- Specialized application services (e.g., rendering, transcoding)
Service Consumers
Any participant in the GRIDNET OS ecosystem that needs to utilize services. This could be:
- End-user applications (UI dApps)
- Other service providers (service composition)
- Automated agents or system components
Decentralized Service Registry
Unlike traditional SOA with centralized registries, GRIDNET OS implements service discovery through:
- Distributed hash tables (DHTs)
- Peer announcements via swarm protocols
- On-chain service registration in the DSM
- Path discovery mechanisms that identify capable service providers
State-Less Channels Incentivization Layer
This is where the State-Less Channels subsystem, as documented in this specification, plays a critical role. It provides:
- Instantaneous micropayments for service utilization
- Fine-grained compensation proportional to service quality and quantity
- Economic incentives that maintain service availability and reliability
- Sybil-resistant reward distribution through mathematical design
1.4.3. Incentivization Mechanism
Traditional SOA implementations typically rely on conventional payment systems or subscription models that operate out-of-band from the actual service calls. These systems face significant limitations in decentralized environments:
- High transaction costs for micropayments
- Lack of real-time compensation
- Vulnerability to free-riding and Sybil attacks
- Inability to handle high-frequency service invocations
GRIDNET OS overcomes these limitations through its State-Less Channels system:
Micropayment-Based Service Economy
Each service interaction can be individually compensated with minimal overhead:
- A UI dApp consuming bandwidth from a WebRTC relay can issue Transmission Tokens for each data packet
- A computational service can receive payment proportional to processing time or complexity
- Content delivery nodes can be rewarded based on bytes transferred and delivery speed
Real-Time Incentives
Payments occur simultaneously with service delivery, creating immediate economic feedback:
- Service providers receive compensation instantly, without waiting for transaction settlement
- Quality of service directly correlates with economic reward
- Resource allocation can dynamically adjust based on real-time incentives
Proportional Compensation
The amount of payment can be precisely calibrated to:
- Value provided by the service
- Resource consumption
- Quality metrics (latency, throughput, availability)
- Position in service paths or chains
1.4.4. SOA Implementation Examples
The Decentralized Incentivized SOA paradigm enables several key services within GRIDNET OS:
Decentralized Web Proxy
-
Service
Access to web content through decentralized routing -
Providers
Nodes with internet connectivity willing to relay web requests -
Incentivization
Transmission Tokens issued per request or data volume -
Benefits
Censorship resistance, privacy enhancement, load distribution
WebRTC Swarms
-
Service
Real-time media streaming and communication -
Providers
Nodes with sufficient bandwidth and network position -
Incentivization
Continuous micropayments based on relay quality and volume -
Benefits
Resilient communication, improved geographic distribution, automatic scaling
Distributed Storage System
-
Service
Data persistence, backup, and retrieval -
Providers
Nodes with available disk space -
Incentivization
Payments for storage duration and retrieval speed -
Benefits
Redundancy without central planning, economic-driven availability
Computational Markets
-
Service
Execution of computationally intensive tasks -
Providers
Nodes with spare CPU/GPU capacity -
Incentivization
Payment based on computational complexity and speed -
Benefits
Distributed load, specialized hardware utilization, market-driven pricing
1.4.5. Advantages Over Traditional SOA
GRIDNET OS’ Decentralized Incentivized SOA provides several key advantages:
Resilience and Redundancy
- No single point of failure for service discovery or delivery
- Automatic adaptation to node failures through economic incentives
- Natural redundancy driven by reward mechanisms
Economic Efficiency
- Services are priced according to actual resource costs
- Reduced overhead compared to traditional payment systems
- No need for complex billing, subscription, or account management
Dynamic Scaling
- Service availability scales with demand through economic signals
- Automatic load balancing through incentive distribution
- Organic growth of service capacity in high-demand areas
Trustless Operation
- No need to trust specific service providers
- Service quality enforced through economic incentives rather than contracts
- Decreased risk of service denial or censorship
1.4.6. Relation to State-Less Channels
The State-Less Channels system described in this documentation serves as the fundamental enabler of the Decentralized Incentivized SOA paradigm. By providing a mechanism for instant, trustless micropayments with minimal overhead, it creates the economic foundation upon which the entire service ecosystem operates.
Key aspects where State-Less Channels directly support the SOA paradigm:
-
Multi-Dimensionality
The M-DTP structure with multiple banks allows service consumers to simultaneously incentivize multiple service providers using a single token pool -
Instant Verification
Services can verify payment validity immediately without on-chain confirmation -
Path Assurance
The PA1, PA2, and PA3 protocols ensure that incentives flow correctly through service chains -
Scalability
Off-chain payments enable high-frequency service invocations without blockchain congestion -
Sybil Resistance
Mathematical design prevents service providers from gaming the system through identity multiplication
Leveraging State-Less Channels via this API offers significant advantages:
-
Scalability & Throughput:
Dramatically increases the potential volume of transactions/rewards that can occur between users and services, far exceeding on-chain TPS limits.
-
Reduced Transaction Costs:
Amortizes the cost of on-chain operations. Instead of paying ERG for every small transfer, costs are primarily associated with the initial Token Pool creation and the final cash-out transaction.
-
Instantaneous (Off-Chain) Transactions:
Off-chain token exchanges happen almost instantly between peers, ideal for real-time applications.
-
Enhanced Privacy:
Individual off-chain transfers within a channel are not broadcast publicly across the DSM. Only the aggregated cash-out transaction is visible on-chain.
-
Sybil-Proof Incentivization:
Builds upon the Sybil-proof mechanics detailed in [1], allowing for fair and secure rewarding of potentially unknown or numerous peers for minuscule actions without the overhead or security risks of individual on-chain micropayments.
1.5. Target Audience
This documentation is primarily intended for
UI dApp developers
building applications on the GRIDNET OS platform. It is particularly relevant for those whose dApps require:
- High-frequency or low-value transactions.
- Real-time reward mechanisms.
- Incentivization for peer-to-peer data exchange or service provision.
- Interaction with systems employing off-chain payments (e.g., integrated games, services).
1.6. Prerequisites
Before diving into this documentation, developers should possess:
- A foundational understanding of
GRIDNET OS concepts
(DSM, IVR, DPT, VM Context, #GridScript basics).
- Proficiency in modern
JavaScript (ES6+)
, including classes, Promises, and async/await.
- Familiarity with
event-driven programming paradigms
, as interaction with the
CVMContext
and the State-Less Channels Manager is heavily reliant on event listeners and callbacks.
2. Core Concepts
This section delves into the fundamental ideas and data structures that underpin the GRIDNET OS State-Less Channels system. Understanding these concepts is essential for effectively utilizing the Off-The-Chain Payment API.
2.1. State-Less vs. State-Full Channels (Brief Comparison)
While various blockchain platforms offer “state channels” for off-chain interactions, GRIDNET OS employs a
State-Less Channel
-
State-Full Channels (e.g., Lightning Network):
Typically require participants to maintain and constantly synchronize complex channel states (balances, commitments) directly with each other. Closing the channel often involves submitting the final agreed-upon state. -
GRIDNET OS State-Less Channels:
Minimize the need for direct, continuous state synchronization between peers during the off-chain phase. The “state” is primarily represented by the pre-funded Token Pool registered on the DSM and the sequence of revealed hashes (Tokens). Peers exchange Transmission Tokens, which act as verifiable claims against the pool. The final state reconciliation happens on-chain only during the cash-out process. This approach simplifies peer interaction and potentially enhances privacy during the off-chain phase.
2.2. Multi-Dimensional Token Pools (M-DTPs): The Foundation

The cornerstone of the State-Less Channels system is the
Multi-Dimensional Token Pool (M-DTP)
. It acts as a secure, on-chain registered escrow of value that enables off-chain payments.
-
Rationale for Multi-Dimensionality:
A single-dimensional pool (a single hash chain) can only reliably serve one off-chain payment stream at a time. If Peer A is paying Peer B using a dimension, Peer A cannot safely start paying Peer C using the same dimension until Peer B has cashed out their received tokens on-chain. This creates a bottleneck. M-DTPs overcome this by providing multiple independent dimensions (Banks), allowing the pool owner to conduct simultaneous, independent off-chain payment streams with multiple different recipients using a single, pre-funded pool. This is crucial for scalability in scenarios like rewarding many data relays or game participants concurrently.
-
Creation:
An M-DTP is typically created through a process involving:
-
Sacrificial Transaction:
The intended pool owner performs an on-chain transaction, effectively locking or “sacrificing” a specific amount of GBU/GNC. The transaction receipt serves as proof of this committed value.
-
Off-Chain Generation:
Using the sacrificial transaction receipt ID and desired parameters (number of dimensions, optionally value per token), the pool structure, including all internal hash chains, is generated off-chain. This critical step requires the owner’s secret
MasterSeedHash
and is usually performed securely by the GRIDNET Token mobile app via a QR Intent process.
-
On-Chain Registration:
The public data of the generated M-DTP (including the final hash for each dimension, total value, owner ID, unique Pool ID, etc., but excluding the MasterSeedHash) is then committed to the DSM.
-
-
On-Chain Representation:
The registered M-DTP data is stored as a file (typically with a
.pool
extension) within the owner’s State Domain on the GRIDNET OS Decentralized File System (DFS). This file can be queried by anyone to verify the pool’s parameters and current on-chain state (e.g., bank usage depths). -
Structure:
Key components of an M-DTP include its unique Token Pool ID, the Owner’s ID, the total value committed, the number of dimensions (Banks), the value assigned to each individual token (usually calculated automatically based on total value, dimensions, and dimension depth), and the array of Banks, each containing its final hash and current usage depth as known by the DSM.
2.3. Token Pool Banks (Dimensions)
Each M-DTP is composed of one or more
Banks
, also referred to as
Dimensions
-
Independent Hash Chains:
Each bank represents an independent, cryptographically generated hash chain.
-
Generating Hashes (Tokens):
All hashes (tokens) within a bank are derived deterministically from a unique dimension seed hash (which is itself derived from the pool’s MasterSeedHash). This is achieved by repeatedly applying a cryptographic hash function (e.g., SHA256):
Hash_i = Hash(Hash_{i-1})
, starting from the dimension seed hash. -
Ceiling Hashes (Final Hash):
The last hash generated in the chain for a dimension (
Hash_N
) is called the ceiling or final hash. This value is stored publicly as part of the M-DTP data on the DSM and acts as the verification anchor for the entire chain within that dimension. -
Tracking Usage Depth:
The DSM maintains a record of how many tokens have been successfully cashed out from each bank. This ‘usage depth’ indicates the highest index ‘i’ for which
Hash_i
has been revealed and accepted on-chain. This is the primary mechanism preventing the same tokens (hashes) from being cashed out twice. -
Bank Status:
A bank can be ‘Active’ (has unspent tokens) or ‘Depleted’ (its usage depth has reached the maximum depth of the dimension).
2.4. Hash Chains and Tokens
-
Tokens as Preimages (Hashes):
In this system, the “tokens” exchanged off-chain are the actual hash values (preimages) from the hash chain within a specific bank.
-
Releasing Tokens (Revealing Hashes):
To spend value, the pool owner reveals hashes in reverse order of their generation. Revealing
Hash_i
requires knowledge of the original dimension seed hash (or a previously revealedHash_{i-k}
). Due to the nature of hash functions, revealingHash_i
automatically proves knowledge of (and implicitly reveals) all subsequent hashesHash_{i+1}
throughHash_N
(the final hash). -
Value Representation:
The value of each single token/hash is uniform across all banks within a pool, determined during creation (e.g., 1 Atto, 1 GBU). The total value released is
NumberOfRevealedHashes * ValuePerToken
.
2.5. MasterSeedHash
-
The Single Secret:
This is the high-entropy secret generated by the owner (typically via the mobile app) when creating the M-DTP. It is the only secret required to derive all dimension seed hashes and subsequently all individual tokens (hashes) within the entire pool.
-
Deriving Dimension Seeds:
A deterministic function (e.g., hashing the MasterSeedHash concatenated with the dimension index) is used to generate the starting Seed Hash for each bank’s independent hash chain.
-
Security Implications:
The MasterSeedHash
must be kept absolutely secret
by the pool owner. Its compromise allows an attacker to generate and potentially spend all remaining value in the pool. It should ideally reside only within a secure environment like the GRIDNET Token mobile app.
2.6. Transmission Tokens (TTs)
While the hashes themselves represent the value, the
Transmission Token (TT)
is the data structure used to communicate the release of this value off-chain. It packages the necessary information for the recipient to verify the claim and eventually cash it out on-chain.
-
Purpose:
To provide proof of released value from a specific bank of a specific Token Pool, indicating the new state (usage depth) of that bank.
-
Structure:
A TT (
CTransmissionToken
object in JS) contains:Revealed Hash
: The latest hash preimage being released in this transfer.Revealed Hashes Count
: How many new tokens this TT represents compared to the previously known state for this bank.Bank Index
: Identifies the specific dimension/bank within the M-DTP.Bank Usage Depth
: The new, total depth reached in this bank after releasing the tokens represented by this TT.Token Pool ID
: Identifies the parent M-DTP.Value
: The total GBU/Atto value represented by this specific TT.(Optional) Recipient ID
: Can specify the intended recipient.(Optional) Signature
: A signature from the Token Pool owner authenticating the TT.
-
Authenticated vs. Unauthenticated TTs:
Unauthenticated TTs rely purely on the hash chain mathematics and on-chain state tracking for security against double-spending by the recipient. Authenticated TTs add a layer of non-repudiation and origin verification, crucial for certain applications or dispute resolution, and mandatory for “Bank Updates” (where the owner reveals hashes to their own web-session to enable spending).
2.7. Transit Pools
The term
Transit Pool
doesn’t represent a distinct, stored data structure in the same way as a Token Pool.
-
Concept:
It refers to the cumulative value represented by a sequence of off-chain Transmission Tokens exchanged since the last on-chain cash-out for a specific bank.
-
On-Chain Representation:
When cashing out, typically only the latest Transmission Token received needs to be submitted to the DSM via the
xTTEx
GridScript command. This single TT contains the final revealed hash and the total usage depth for that bank, implicitly representing the entire “transit pool” of value accumulated off-chain since the last settlement.
2.8. Cashing Out
This is the process of converting the off-chain value represented by Transmission Tokens back into on-chain GBU/GNC balance.
-
The Process:
The recipient (or the
CStateLessChannelsManager
autonomously) submits the latest valid Transmission Token for a specific bank/pool to the DSM via anxTTEx
GridScript instruction, usually packaged within a standard on-chain transaction. -
On-Chain Verification:
The DSM node executing the transaction verifies the TT against the on-chain state of the corresponding M-DTP file:
- Checks if the
Token Pool ID
exists and the pool isActive
. - Checks if the
Bank Index
is valid. - Checks if the
Bank Usage Depth
in the TT is greater than the currently recorded depth for that bank (prevents replay/double-spend). - Cryptographically verifies the hash chain segment by repeatedly hashing the
Revealed Hash
forRevealed Hashes Count
times and checking if it matches the currently recorded final hash for that bank state. - (If TT is authenticated) Verifies the signature against the Token Pool owner’s public key.
- Checks if the
-
State Update:
If verification succeeds, the DSM updates the bank’s usage depth in the M-DTP file on DFS and credits the
Value
specified in the TT to the designated recipient’s on-chain account balance. -
Associated Costs:
Executing the
xTTEx
command and the encompassing transaction incurs standard ERG costs on the DSM.
2.9. StateLessChannel Object (CStateLessChannel
)
This JavaScript class serves as the primary high-level abstraction for UI dApp developers interacting with the off-chain payment system.
-
JavaScript Abstraction:
It encapsulates the logic for managing interaction with a single Token Pool, hiding complexities like direct BER encoding/decoding or GridScript command formulation.
-
Relationship to On-Chain Data:
Each
CStateLessChannel
instance is associated with a specificToken Pool ID
. It maintains a local cache (mTokenPool
) of the pool’s data, which needs to be periodically synchronized (synchronize()
) with the state stored on the DSM/DFS. -
Ingress vs. Outgress:
The direction is determined relative to the local user interacting via the UI dApp:
-
Ingress Channel:
Represents a pool owned by another party from which the local user receives payments/rewards (e.g., Snake game rewards). The dApp typically uses this to validate incoming TTs and initiate cash-outs.
-
Outgress Channel:
Represents a pool owned by the local user used to send payments to others. The dApp uses this to generate and potentially deliver TTs.
-
- Architecture and Interaction Flow
Understanding how the different parts of GRIDNET OS interact to enable State-Less Channels is key to building robust UI dApps. This section outlines the primary components involved and the typical flow of information and control between them.
3. Architecture and Interaction Flow
Understanding how the different parts of GRIDNET OS interact to enable State-Less Channels is key to building robust UI dApps. This section outlines the primary components involved and the typical flow of information and control between them.
3.1. System Components Overview
The State-Less Channels sub-system involves a collaboration between several distinct components operating at different levels:
-
UI dApp (e.g., Wallet, Game):
The user-facing application built using web technologies (HTML, CSS, JS) running within a
CWindow
instance. It initiates actions (like sending a payment or requesting a cash-out) and subscribes to events to update its state and display information (e.g., received tokens, channel balance). UI dApps only interact with the system via theCVMContext
. -
JavaScript VM Context (
CVMContext
):The central singleton object acting as the sole gateway between UI dApps and the underlying GRIDNET OS functionalities. It manages communication with Core Nodes, handles event dispatching, provides access to various managers (like the Channels Manager), and facilitates the execution of GridScript commands originating from dApps.
-
State-Less Channels Manager (
CStateLessChannelsManager
):A singleton service, accessible via
CVMContext
, responsible for the overall management of all State-Less Channel instances within the user’s session. It handles incoming network events related to channels, routes data to the appropriate channel objects, manages a local cache of known channels, performs autonomous tasks like synchronization and automatic cash-outs, and exposes the primary API for dApps to interact with the channel system. -
State-Less Channel Objects (
CStateLessChannel
):JavaScript objects, managed by the
CStateLessChannelsManager
, each representing a single, specific off-chain payment channel linked to a unique Multi-Dimensional Token Pool ID. It encapsulates the logic for validating/generating Transmission Tokens, interacting with its cachedCTokenPool
data, and initiating requests for synchronization or cashing out. -
Token Pool Objects (
CTokenPool
,CTokenPoolBank
):JavaScript data structures representing the M-DTP and its individual dimensions (Banks) as loaded from the DSM or constructed locally. They contain the core cryptographic state (final hashes, usage depths) and logic for hash chain verification used by
CStateLessChannel
. -
Transmission Token Objects (
CTransmissionToken
):JavaScript data structure representing the off-chain payment instrument, containing the revealed hash, value, bank details, etc.
-
GRIDNET Core Nodes:
The distributed network nodes that maintain the Decentralized State Machine (DSM), execute #GridScript commands (like
xTTEx
for cashing out), manage the Decentralized File System (DFS) where Token Pool data is stored, and handle underlying network communication (UDT, WebSockets, etc.). -
GRIDNET Token Mobile App:
The user’s secure companion application. It holds sensitive keys (like the MasterSeedHash needed for pool generation), performs cryptographic operations (signing transactions, generating hashes/tokens securely), and interacts with Core Nodes via QR Intents and secure, often onion-routed, connections to authorize critical actions.
3.2. Interaction Diagram (High-Level)
(Conceptual Description – A visual diagram would accompany this in full documentation)
Imagine a layered architecture:
-
UI Layer:
The UI dApp presents information and captures user intent.
-
API Gateway Layer:
The
CVMContext
receives requests from the dApp and dispatches events back to it. -
Service Layer:
The
CStateLessChannelsManager
resides here, accessed viaCVMContext
. It orchestrates channel operations. -
Object Layer:
CStateLessChannel
,CTokenPool
,CTransmissionToken
objects manage the state and logic for individual channels/tokens. -
Core Layer:
GRIDNET Core Nodes execute commands and manage the persistent DSM/DFS state.
-
Security Layer:
The Mobile App handles sensitive key operations and authorizations.
Communication flows vertically (e.g., dApp -> CVMContext -> Manager) and horizontally (e.g., CVMContext <-> Core Node; Mobile App <-> Core Node). Events propagate upwards (Core -> CVMContext -> Manager -> Channel -> dApp).
3.3. Role of CVMContext
Within the State-Less Channels system, CVMContext
acts as the essential intermediary and event bus:
-
Singleton Access Point:
It’s the only way for a UI dApp to access the
CStateLessChannelsManager
(getChannelsManager()
) and other OS services. -
Event Dispatcher:
It receives low-level events from Core Nodes (e.g., via the VM Meta-Data protocol containing new TTs or pool data, or via DFS message results) and dispatches them to registered listeners, including the
CStateLessChannelsManager
. -
Processing GridScript Commands:
It takes requests formulated by the Manager or Channel objects (often wrapped in
CVMMetaGenerator
objects) and sends them to the appropriate Core Node for execution using methods likeprocessVMMetaDataKF
. This is how cash-out (xTTEx
) and pool synchronization (getPoolEx
) commands are initiated.
3.4. Role of CStateLessChannelsManager
The Manager is the central coordinator for all off-chain payment activity:
-
Central Hub:
Manages the lifecycle and collection of all active
CStateLessChannel
instances for the current user session. -
Event Routing:
Listens for relevant events from
CVMContext
(newVMMetaDataCallback
,newDFSMsgCallback
). It parses incoming data (e.g., deserializing TTs or Token Pools from BER encoding) and routes it to the correctCStateLessChannel
object based on the Token Pool ID. -
Autonomous Operations:
Runs its own internal timer (
controllerThreadF
) to periodically trigger channel synchronization (syncChannels
) and check conditions for automatic cash-outs (cashOutChannels
) based on configured thresholds (mCashOutChannelAfterTimout
,mCashOutChannelsAtValue
). -
Local Cache:
Maintains the
mChannels
array, the primary list of known/managed channel objects. -
API Provider:
Exposes functions (like
findChannelByID
,syncAgentChannels
) that UI dApps access through theCVMContext
to interact with the channel system at a higher level.
3.5. Role of CStateLessChannel
Objects
Each instance represents and manages a single channel:
-
Channel State:
Holds the locally cached
CTokenPool
object representing the channel’s state (final hashes, usage depths, etc.) as last known. -
Token Pool Interaction:
Contains references to the underlying
CTokenPool
andCTokenPoolBank
logic for calculations. -
Core Logic:
Implements the primary off-chain logic:
validateTT
: Verifies incoming Transmission Tokens against the cached pool state.getTTForPeer
/getTTWorthValue
: Generates new Transmission Tokens for sending payments (if it’s an Outgress channel).
-
Initiating Operations:
Triggers requests via the Manager/CVMContext for:
synchronize()
: To update its cachedCTokenPool
from the DSM/DFS.cashOut()
: To initiate the on-chain settlement process.
3.6. Interaction with the Decentralized State Machine (DSM)
The DSM and its associated DFS are the persistent, consensus-driven backend:
-
Storing Token Pool Data:
Public M-DTP data (final hashes, total value, owner, ID, current bank depths) is stored as files within the DFS, typically under the owner’s State Domain (e.g.,
/OwnerID/TokenPools/PoolID.pool
). -
Executing Cash-Out Transactions:
Core Nodes execute the
xTTEx
#GridScript command submitted in a transaction. This involves reading the relevant Token Pool file from DFS, performing the cryptographic verification of the submitted TT, updating the bank’s usage depth in the pool file, and crediting the recipient’s on-chain balance. -
Reading Token Pool State:
The
getPoolEx
#GridScript command reads the.pool
file from DFS and returns its BER-encoded content, enabling synchronization.
3.7. Interaction with the Mobile App
The mobile app acts as the user’s secure hardware wallet and authorization device:
-
QR Intents:
Facilitates secure communication for operations requiring the user’s private key or the secret MasterSeedHash.
-
Pool Generation:
The computationally intensive and security-critical generation of M-DTP hash chains from the MasterSeedHash happens only within the mobile app in response to a
genPool
QR Intent. The app returns the disarmed (public) pool data. -
MasterSeedHash Management:
Securely stores and manages the user’s MasterSeedHash(es).
-
Signing:
Signs critical on-chain transactions (like the one registering a new pool or potentially cash-out requests if configured) via QR Intents.
-
Providing “Bank Updates”:
Can securely reveal specific hash preimages (packaged as authenticated TTs with
isBankUpdate=true
) to the user’s web session via QR Intents, allowing the web UI/dApp to spend pre-funded value from an Outgress channel without exposing the MasterSeedHash.
3.8. Event-Driven Communication Flow (Simplified Sequences)
-
Receiving a TT (e.g., Peer-to-Peer or Game Reward):
- TT arrives at a Core Node (e.g., via WebRTC Swarm or direct connection).
- Core Node may wrap it in VM Meta-Data and send it to the relevant
CVMContext
. CVMContext
receives the VM Meta-Data message.CVMContext
dispatches the event to its listeners, includingCStateLessChannelsManager
.Manager.newVMMetaDataCallback
parses the message, identifies it as a TT/BankUpdate, deserializes theCTransmissionToken
.- Manager finds the corresponding
CStateLessChannel
object by Pool ID (creating one if necessary viaonNewChannel
after fetching pool data). - Manager calls
channel.validateTT
(potentially updating the channel’s internal state). - Manager raises the
onTT
andonChannelNewState
events. CVMContext
dispatches these higher-level events.- The UI dApp’s registered listener (e.g.,
addNewTTListener
) receives the event and updates its UI.
-
Cashing Out an Ingress Token:
- UI dApp determines the need to cash out (e.g., user click, autonomous logic).
- dApp retrieves the relevant
CStateLessChannel
(viamanager.findChannelByID
). - dApp calls
channel.cashOut(latestToken, myRecipientID)
. channel.cashOut
formulates a #GridScriptxTTEx
command.- Channel requests execution via
CVMContext.processVMMetaDataKF
. CVMContext
sends the command to a Core Node.- Core Node executes the GridScript, which involves DSM interaction (reading pool, verifying TT, updating pool state, updating balance). This occurs within an on-chain transaction context.
- Confirmation/Result might eventually propagate back via blockchain events or receipt listeners.
-
Token Pool Creation (via Mobile App):
- User/dApp initiates pool creation (e.g., in Wallet UI).
- UI dApp triggers a
genPool
request viaCVMContext.processVMMetaDataKF
(containing sacrificial receipt ID, dimensions, etc.). - Core Node receives request, generates a specific QR Intent.
CVMContext
receives the QR Intent data via event and displays it (using UI dApp helpers).- User scans QR with Mobile App.
- Mobile App securely generates the pool using MasterSeedHash, performs necessary signing.
- Mobile App sends the disarmed pool data + authorization back to the originating Core Node (via secure onion routing).
- Core Node receives the pool data.
- Core Node sends the pool data back to
CVMContext
via VM Meta-Data. CVMContext
dispatches ->Manager.newVMMetaDataCallback
receives pool data.- Manager potentially creates a new
CStateLessChannel
instance. - Manager raises
onNewTokenPool
/onNewChannel
events ->CVMContext
dispatches -> UI dApp is notified. - (Separately) User/dApp initiates the
CT
(Commit Transaction) via QR to get the pool registered on-chain via DFS.
4. The CStateLessChannelsManager
API (JavaScript)
The CStateLessChannelsManager
is the primary interface for UI dApps to interact with the State-Less Channels system. It orchestrates the discovery, synchronization, and management of off-chain channels and related data. As a singleton service within the CVMContext
, it ensures a consistent view and handling of channel operations across all UI dApps in a user session.
4.1. Accessing the Manager
Being a singleton managed by the CVMContext
, you access the CStateLessChannelsManager
instance as follows:
// Get the CVMContext instance (singleton) const vmContext = CVMContext.getInstance(); // Get the State-Less Channels Manager instance const channelsManager = vmContext.getChannelsManager; // Now you can call methods on channelsManager // e.g., channelsManager.findChannelByID(...)
4.2. Initialization and Lifecycle
The manager’s core functionality, including its autonomous operations, relies on an internal controller thread and event listeners connected to the CVMContext
.
-
initialize()
:-
Purpose:
Starts the manager’s internal controller thread (a JavaScript timer loop) responsible for periodic tasks like channel synchronization and checking auto-cash-out conditions. It also registers the necessary internal listeners (
newVMMetaDataCallback
,newDFSMsgCallback
) with theCVMContext
. -
Usage:
This method is typically called automatically during the
CVMContext
‘s own initialization sequence. UI dApp developers usually do not need to call this directly. -
Returns:
void
.
-
-
destroy()
:-
Purpose:
Stops the internal controller thread, preventing further autonomous operations. It does not automatically unregister event listeners registered by dApps.
-
Usage:
Called when the
CVMContext
is shutting down or if explicitly needed to halt the manager’s background activity. UI dApp developers rarely need to call this. -
Returns:
void
.
-
4.3. Channel Discovery and Management
These methods allow dApps to query the manager’s cache of known State-Less Channels.
-
findChannelByID(channelID, allowFriendlyID = true)
:-
Purpose:
Searches the manager’s internal list (
mChannels
) for aCStateLessChannel
object matching the provided ID. -
Parameters:
channelID
(ArrayBuffer | String): The unique Token Pool ID (usually Base58Check encoded string) or a Friendly ID.allowFriendlyID
(Boolean): Iftrue
, the search will also match against the channel’s Friendly ID.
-
Returns:
The
CStateLessChannel
object if found, otherwisenull
. -
Example:
const poolId = "2NPe49vveUrhF8WE5trVSTKRzcKPSFVdJqaFRBMKPUk"; // Example ID const channel = channelsManager.findChannelByID(poolId); if (channel && channel.getIsReadyForVerification) { console.log("Channel found and ready:", channel.getFriendlyID); } else if (channel) { console.log("Channel found but needs sync:", channel.getFriendlyID); // channel.synchronize(); // Optionally trigger sync } else { console.log("Channel not found locally."); // Might need to trigger discovery via syncAgentChannels if owner is known }
-
-
findChannelsByOwner(ownerID, allowFriendlyID = true)
:-
Purpose:
Finds all channels in the manager’s cache whose underlying Token Pool is owned by the specified
ownerID
. -
Parameters:
ownerID
(ArrayBuffer | String): The State Domain ID (or potentially Friendly ID ifallowFriendlyID
is true, though primarily designed for domain IDs) of the owner.allowFriendlyID
(Boolean): Allows matchingownerID
against friendly IDs of the owner (less common use case).
-
Returns:
An
Array
ofCStateLessChannel
objects owned by the specified ID, or an empty array if none are found.
-
-
getChannelIDs(mode = eChannelDirection.both, allowFriendlyIDs = true)
:-
Purpose:
Retrieves a list of IDs for all channels currently managed by the instance, filterable by direction (relative to the local user).
-
Parameters:
mode
(eChannelDirection
enum:ingress
,outgress
,both
): Filters the channels based on their direction.ingress
means the local user receives from the channel,outgress
means the local user sends from the channel (owns the pool).allowFriendlyIDs
(Boolean): Iftrue
and a channel has a Friendly ID, that ID is returned instead of the raw Token Pool ID.
-
Returns:
An
Array
of strings (Channel IDs or Friendly IDs).
-
4.4. Synchronization
These methods are used to update the local cache of channel states with the latest information from the DSM/DFS.
-
syncAgentChannels(agentID)
:-
Purpose:
Initiates a discovery and synchronization process for all State-Less Channels associated with a specific
agentID
(typically a State Domain ID). It does this by triggering a DFSdoCD
(Change Directory and List) command for the standard Token Pool directory (/agentID/TokenPools/
) on a GRIDNET Core node. The results (list of.pool
files) arrive asynchronously via thenewDFSMsgCallback
. The manager then typically queuesdoGetFile
requests for each pool found. -
Parameters:
agentID
(ArrayBuffer | String): The State Domain ID of the agent whose channels should be synced.
-
Returns:
Boolean
indicating if the initial DFS request was successfully sent (does not guarantee channels were found or synced).
-
-
syncChannels(forceIt = false)
:-
Purpose:
Iterates through all currently managed channels (
mChannels
) and calls thesynchronize()
method on each one ifforceIt
is true or if the time since its last sync exceedsmChannelSyncInterval
. -
Parameters:
forceIt
(Boolean): Iftrue
, forces synchronization regardless of the last sync time.
-
Returns:
void
.
-
-
mChannelSyncInterval
(Property):-
Purpose:
A configuration value (in seconds, default 600) determining how often the manager’s controller thread attempts to automatically synchronize managed channels.
-
4.5. Autonomous Operations
The manager performs background tasks based on configured parameters.
-
Cash-out Logic:
The internal controller thread (
controllerThreadF
) periodically callscashOutChannels()
. -
cashOutChannels(forceIt = false, autoCommit = true, channelIDsp = [], abortOnNotReady = true)
:-
Purpose:
(Primarily internal, but potentially callable) Checks managed ingress channels against configured thresholds (
mCashOutChannelsAtValue
,mCashOutChannelAfterTimout
) or forces cash-out ifforceIt
is true. For qualifying channels, it formulates and queuesxTTEx
#GridScript commands viaCVMContext
to cash out the latest knownmRecentToken
. -
Parameters:
(See method signature for details, usually called internally without parameters).
-
Returns:
Boolean
indicating if any cash-out operations were initiated.
-
-
mCashOutChannelsAtValue
(Property):Configurable threshold (GBU/Atto value) for auto cash-out.
-
mCashOutChannelAfterTimout
(Property):Configurable time threshold (seconds) for auto cash-out.
4.6. Event Subscription API
UI dApps subscribe to these events to react to changes in the State-Less Channel ecosystem. All listener callbacks should ideally be async
functions.
-
addNewTTListener(eventListener, appID = 0)
:-
Triggered:
When the Manager successfully processes an incoming
CTransmissionToken
(either a standard payment/reward or a Bank Update). -
Callback Argument:
An object
{ token: CTransmissionToken, isBankUpdate: Boolean }
. -
Purpose:
Allows dApps to react to received off-chain value or bank updates.
-
Example:
async function handleNewTT(event) { console.log(`Received TT for pool ${gTools.encodeBase58Check(event.token.getTokenPoolID())}`); if (event.isBankUpdate) { console.log("It's a bank update, refreshing spendable balance."); // Potentially update UI related to sending capabilities } else { const channel = channelsManager.findChannelByID(event.token.getTokenPoolID()); const accumulated = channel ? await channel.getAccumulatedValue() : 0n; console.log(`Value in this TT: ${event.token.getValue()}. Total accumulated off-chain: ${accumulated}`); // Update UI with received value } } channelsManager.addNewTTListener(handleNewTT.bind(this), this.mID);
-
-
addNewTokenPoolListener(eventListener, appID = 0)
:-
Triggered:
When the Manager discovers or receives data for a
CTokenPool
object for the first time in the current session (often as a result ofsyncAgentChannels
or direct VM Meta-Data). Note:addNewStateChannelListener
is usually triggered subsequently or concurrently. -
Callback Argument:
An object
{ pool: CTokenPool, requestID: Number }
. -
Purpose:
Informs dApps about the existence and initial state of a Token Pool.
-
-
addNewStateChannelListener(eventListener, appID = 0)
:-
Triggered:
When the Manager creates a new
CStateLessChannel
object instance to manage a discovered Token Pool. This signifies the channel is now being actively tracked. -
Callback Argument:
An object
{ channel: CStateLessChannel, requestID: Number }
. -
Purpose:
Allows dApps to know when a new channel becomes managed and potentially start interacting with it or updating UI lists.
-
-
addNewTransitPoolListener(eventListener, appID = 0)
:-
Clarification Needed:
The concept of a “Transit Pool” event is less distinct in the current State-Less model compared to the underlying
CTransmissionToken
andCTokenPool
updates. This listener’s specific trigger condition might overlap significantly with TT or Receipt events.It’s likely related to the completion or receipt of an on-chain cash-out (
xTTEx
) transaction.Developers should currently rely more on
addNewTTListener
for off-chain updates andaddNewReceiptListener
for on-chain settlement confirmation. Further clarification from core implementation might be needed.
-
-
addNewReceiptListener(eventListener, appID = 0)
:-
Triggered:
When a transaction receipt arrives via
CVMContext
that corresponds to a previously initiated State-Less Channel operation (e.g., a cash-outxTTEx
transaction or pool registration). The manager needs internal logic to correlate receipts to channel operations. -
Callback Argument:
An object
{ receipt: CReceipt }
(Structure ofCReceipt
needs definition – likely contains TX ID, status, block info). -
Purpose:
Allows dApps to confirm that an on-chain action related to a channel (like cashing out) has been successfully finalized on the DSM.
-
-
addStateChannelNewStateListener(eventListener, appID = 0)
:-
Triggered:
When the Manager processes an event (like a new TT or a successful pool sync) that results in a change to the cached state of a managed
CStateLessChannel
(e.g., itsmTokenPool
‘s bank depths are updated). -
Callback Argument:
An object
{ channel: CStateLessChannel, newState: Object, previousState: Object | null }
. The state objects contain key metrics likeavailableAssets
,spendableAssets
,accumulatedAssets
. -
Purpose:
Enables dApps to reactively update UI elements displaying channel balances or status based on processed off-chain or synchronized on-chain data.
-
4.7. Internal Callbacks (Manager’s Perspective)
These are the methods the Manager uses to listen to the CVMContext
. UI dApp developers typically don’t interact with these directly but understanding them helps clarify the flow.
-
newVMMetaDataCallback(metaMsg)
:- Handles incoming VM Meta-Data messages.
- Parses the data using
CVMMetaParser
. - Looks specifically for
eVMMetaSectionType.stateLessChannels
sections. - Deserializes elements like
CTransmissionToken
orCTokenPool
based oneVMMetaEntryType.StateLessChannelsElement
andeStateLessChannelsElementType
. - Calls internal
onTT
oronTokenPoolReceived
methods.
-
newDFSMsgCallback(dfsMsg)
:- Handles responses to DFS requests initiated by the Manager or Channels (e.g.,
doCD
,doGetFile
). - Parses the DFS message and potential embedded VM Meta-Data (
dfsMsg.getData1
). - If it’s a directory listing (
doCD
response), it might triggerdoGetFile
requests for found.pool
files. - If it’s file content (
doGetFile
response for a.pool
file), it deserializes theCTokenPool
. - Calls internal
onTokenPoolReceived
.
- Handles responses to DFS requests initiated by the Manager or Channels (e.g.,
-
Internal Event Raisers (
onTT
,onNewTokenPool
, etc.):- These private/internal methods within the Manager are called by the callbacks above after successfully processing data.
- They perform core logic (e.g., finding/updating the relevant channel object).
- They are responsible for finally raising the public events (e.g.,
addNewTTListener
,addNewStateChannelListener
) that UI dApps subscribe to.
5. The CStateLessChannel
API (JavaScript – Key Aspects)
While the CStateLessChannelsManager
provides overall coordination, the CStateLessChannel
class represents a single, specific channel instance. UI dApps typically obtain instances of this class from the manager (e.g., via findChannelByID
) rather than constructing them directly. This section highlights the most important methods and properties developers will interact with.
5.1. Object Lifecycle and State
These methods and properties relate to the creation, readiness, and basic information of a channel instance.
-
constructor(tokenPoolID = null)
:-
Purpose:
Creates a new
CStateLessChannel
object instance associated with a specific Token Pool ID. -
Parameters:
tokenPoolID
(ArrayBuffer | String): The unique ID of the Multi-Dimensional Token Pool this channel represents.
-
Note:
While constructible, instances are typically managed and provided by the
CStateLessChannelsManager
. Direct instantiation by a dApp is uncommon.
-
-
synchronize()
:-
Purpose:
Initiates a request (via the Manager and
CVMContext
) to fetch the latest state data for the associated Token Pool from the DSM/DFS. This updates the channel’s internal cachedmTokenPool
object. -
Parameters:
None.
-
Returns:
Boolean
indicating if the synchronization request was successfully sent. The actual data arrives asynchronously via events. -
Usage:
Call this when you need the most up-to-date on-chain state for a channel, especially before critical operations like validating a newly received high-value token or attempting a cash-out, or if the channel’s
getIsReadyForVerification
is false. Be mindful of rate limits.
-
-
getIsReadyForVerification
(Getter):-
Purpose:
Checks if the channel instance has successfully loaded its corresponding
CTokenPool
data from the DSM/DFS (i.e.,mTokenPool
is not null). -
Parameters:
None.
-
Returns:
Boolean
.true
if the pool data is loaded and TT validation can be attempted,false
otherwise.
-
-
getID()
(Getter):-
Purpose:
Returns the unique Token Pool ID (as an
ArrayBuffer
) associated with this channel.
-
-
getFriendlyID()
(Getter):-
Purpose:
Returns the human-readable friendly identifier assigned to the Token Pool, if one exists.
-
Returns:
String
, potentially empty if no friendly ID is set.
-
-
getTokenPool()
(Getter):-
Purpose:
Returns the internally cached
CTokenPool
object instance containing the detailed state (banks, depths, etc.) for this channel. -
Returns:
CTokenPool
object ornull
if not yet synchronized.
-
-
getIsOutgress
(Getter):-
Purpose:
Determines if this channel represents an outbound payment stream from the perspective of the currently logged-in user. This means the logged-in user owns the underlying Token Pool. It compares the pool’s owner ID with the user’s ID from
CVMContext
. -
Returns:
Boolean
.true
if the local user owns the pool (Outgress),false
if someone else owns it (Ingress), or potentiallyfalse
or throws an error if the user isn’t logged in or pool data is unavailable.
-
-
Timestamps (
getLastTimeUsed()
,getLastTimeUpdated()
,getLastTimeCashedOut()
,getTimeSince...()
methods):-
Purpose:
Provide access to timestamps (Unix epoch seconds) tracking key events in the channel’s lifecycle within the current session.
getTimeSince...()
methods return the duration in seconds since that event. -
Note:
These are primarily informative for the local session and may reset when the session restarts. The core on-chain state is managed by the DSM.
-
-
ping(type = eChannelLocalPingType.lastUpdate)
:-
Purpose:
(Mostly internal) Updates the internal timestamp specified by the
type
enum (update
,lastUsed
,lastSync
,lastCashOut
).
-
5.2. Generating Transmission Tokens
These methods are used by UI dApps managing
Outgress
channels (where the local user owns the pool) to create TTs for sending off-chain payments.
-
getTTForPeer(value, peerID = '', markUsed = true, reassignDimensionsIfNeeded = true)
:-
Purpose:
High-level method to generate a
CTransmissionToken
for a specific value destined for a specific peer. It handles the logic of finding an appropriate, available bank (dimension) within the pool, potentially assigning a free bank to the peer if none is currently assigned or if the previously assigned one is depleted/insufficient. -
Parameters:
value
(BigInt | Number): The GBU/Atto value the token should represent.peerID
(ArrayBuffer | String): The ID of the intended recipient. This helps the channel manage bank assignments. If empty, it attempts to find the first available unassigned bank with sufficient value.markUsed
(Boolean): Iftrue
(default), updates the internal state of the chosen bank as if the token was spent locally (decrements available value, increments usage depth). Set tofalse
only for inspection/simulation.reassignDimensionsIfNeeded
(Boolean): Iftrue
(default), allows the method to automatically assign a new free bank to the peer if their currently assigned bank is unsuitable (e.g., depleted).
-
Returns:
A
CTransmissionToken
object on success,null
if no suitable bank/dimension could be found or assigned, or if the pool lacks sufficient total spendable value.
-
-
getTTWorthValue(value, bankID = -1, markUsed = true)
:-
Purpose:
Lower-level method to generate a
CTransmissionToken
for a specific value from a specific bank, or the first available unassigned bank ifbankID
is -1. It does not perform automatic peer-to-bank assignment management likegetTTForPeer
. -
Parameters:
value
(BigInt | Number): The GBU/Atto value.bankID
(Number): The index of the specific dimension/bank to use. If -1, it searches for the first unassigned bank with enough value.markUsed
(Boolean): Updates the internal bank state iftrue
.
-
Returns:
A
CTransmissionToken
object on success,null
if the specified (or first found) bank lacks sufficient value or is invalid.
-
5.3. Validating Transmission Tokens
These methods are primarily used by UI dApps managing
Ingress
channels to verify incoming TTs and check available balances.
-
validateTT(token, updateState = true, isBankUpdate = false)
:-
Purpose:
The core method to validate an incoming
CTransmissionToken
against the channel’s cachedCTokenPool
state. It performs the necessary cryptographic hash chain verification and checks if the token represents a valid progression from the currently known usage depth of the bank. -
Parameters:
token
(CTransmissionToken
): The token object to validate.updateState
(Boolean): Iftrue
(default) and the token is valid, updates the internal cached state of theCTokenPool
(specifically, the bank’smCurrentDepth
andmCurrentFinalHash
). This should generally be true when processing received payments.isBankUpdate
(Boolean): Iftrue
, indicates this token is a special update from the pool owner (via mobile app) meant to reveal more spendable assets from an outgress channel, rather than representing a payment received. It updates the bank’s pre-cached seed hash instead of its usage depth.
-
Returns:
BigInt
representing the value of the newly validated token segment (i.e.,token.getRevealedHashesCount() * tokenPool.getSingleTokenValue()
) if valid,0n
if invalid (e.g., double-spend attempt, hash mismatch), or-1n
if the channel is not ready (mTokenPool
is null).
-
-
getAccumulatedValue()
(Getter):-
Purpose:
Calculates the total value represented by the last known valid
CTransmissionToken
(mRecentToken
) received for this channel in the current session, relative to the pool’s initial state (or last cash-out state if known locally). Essentially, it checks how much value themRecentToken
represents if it were to be cashed out now. -
Returns:
BigInt
representing the potential cash-out value based on the last token,0n
if no token has been received or validated, or the pool data isn’t ready.
-
-
getLocallySpendableAssets()
(Getter):-
Purpose:
Relevant primarily for
Outgress
channels. Calculates the amount of value the local user can currently spend off-chain without needing further interaction with their mobile app. This relies on the depth of pre-cached seed hashes potentially provided by the mobile app as “Bank Updates”.
-
Returns:
BigInt
representing the spendable value based on pre-cached data,0n
if no pre-cached hashes are available beyond the current on-chain usage depth.
-
5.4. Cashing Out
This method initiates the process of settling the off-chain value onto the main blockchain.
-
cashOut(token, awardeeID, autoCommit = false)
:-
Purpose:
Creates and sends the #GridScript
xTTEx
command (via the Manager andCVMContext
) to a Core Node to initiate the on-chain cash-out process for the value represented by the providedtoken
. -
Parameters:
token
(CTransmissionToken
): The latest valid token received for the channel/bank that is to be cashed out. This token implicitly covers all value since the last on-chain settlement for that bank.awardeeID
(ArrayBuffer | String): The State Domain ID of the entity who should receive the funds on-chain (usually the local user for an Ingress channel).autoCommit
(Boolean): Iftrue
, attempts to immediately trigger a full commit procedure viaCVMContext.commit()
after queueing thexTTEx
command (less common, usually commit is handled separately).
-
Returns:
Boolean
indicating if thexTTEx
command was successfully sent to theCVMContext
for processing. It does not guarantee the cash-out succeeded on-chain. Confirmation comes via events (addNewReceiptListener
).
-
5.5. Bank (Dimension) Management
These methods provide finer control over how peers are associated with specific dimensions, primarily useful for Outgress channels in complex reward scenarios.
-
assignPeerToBank(peerID, bankID)
:Explicitly associates a peer with a bank ID in the channel’s internal map (
mRecipientsToBanks
). -
isBankInUse(bankID)
:Checks if a specific bank ID is currently assigned to any peer in the internal map.
-
findBankForUser(peerID)
:Looks up the
CTokenPoolBank
object currently assigned to the given peer ID. -
freeBank(bankID)
:Removes any association for the given bank ID from the internal map, making it available for assignment to another peer.
I’ll convert the content into HTML with the same formatting style as your existing HTML fragment. Here’s the converted HTML:
5.6. Token Pool Synchronization
5.6.1. When Token Pool Updates Are Needed
Token Pools stored in the web browser’s environment need to be synchronized with their authoritative state on the DSM/DFS under several circumstances:
- Initial Discovery: When a UI dApp first encounters a Token Pool ID, it needs to retrieve the pool’s data to create a local CTokenPool instance.
- Periodic Refresh: The pool’s on-chain state may change due to other participants’ actions (cash-outs) occurring in parallel sessions.
- Before Critical Operations: Prior to sending or validating high-value Transmission Tokens, or before cashing out.
- After Suspected Staleness: If a TT validation or generation fails unexpectedly, the local pool cache might be out of date.
- After Cash-Out Operations: After a successful cash-out transaction, the pool’s bank usage depths have changed on-chain.
5.6.2. Synchronization Mechanism
The synchronization process involves the following steps:
// Within a UI dApp async function updatePoolState(channelId) { const manager = this.mVMContext.getChannelsManager; const channel = manager.findChannelByID(channelId); if (channel) { // Trigger synchronization with on-chain state const syncSuccess = await channel.synchronize(); if (syncSuccess) { console.log("Channel synchronized successfully"); // Update UI with fresh data this.updateChannelDisplay(channelId); } else { console.warn("Channel synchronization failed"); } } }
Under the hood, synchronize() calls getPoolEx via CVMContext to fetch the latest Token Pool data from the DFS. This updates the bank usage depths and other state information to match the on-chain reality.
5.6.3. Automatic vs. Manual Synchronization
The CStateLessChannelsManager provides both automatic and manual synchronization options:
-
Automatic Synchronization
- The manager’s internal controller thread (controllerThreadF) periodically calls syncChannels() at intervals defined by mChannelSyncInterval (default: 600 seconds).
- This ensures all managed channels are kept reasonably up-to-date without developer intervention.
-
Manual Synchronization
- Developers should manually trigger synchronization in these cases:
- Before Sending Value: Before generating a new TT to ensure the channel has sufficient spendable value:
// Check readiness before sending if (!channel.getIsReadyForVerification()) { await channel.synchronize(); } const tt = await channel.getTTWorthValue(valueAtto, recipientId);
- Before Cashing Out: To ensure the most recent state is considered:
await channel.synchronize(); const accumulatedValue = await channel.getAccumulatedValue(); if (accumulatedValue > 0n) { channel.cashOut(latestToken, myDomainID); }
- After Failed Operations: If TT validation or generation fails unexpectedly:
if (await channel.validateTT(token) === 0n) { // Token appears invalid, synchronize and try again await channel.synchronize(); const validationResult = await channel.validateTT(token); // Handle result... }
5.6.4. Checking Synchronization Status
Before performing critical operations, UI dApps should check if a channel is ready for verification:
const channel = manager.findChannelByID(channelId); if (channel) { if (channel.getIsReadyForVerification()) { // Channel's local cache is valid and ready for use proceedWithOperation(); } else { // Channel needs synchronization await channel.synchronize(); if (channel.getIsReadyForVerification()) { proceedWithOperation(); } else { // Still not ready, handle error case showError("Channel data could not be synchronized"); } } }
5.6.5. Synchronization and Events
When synchronization completes, the CStateLessChannelsManager raises appropriate events:
- If significant state changes occurred, onChannelNewState will be triggered
UI dApps can react to these events to update their display:
// Register for state changes manager.addStateChannelNewStateListener(this.handleChannelStateChange.bind(this), this.mID); // Handle state changes, including those from synchronization async handleChannelStateChange(event) { const channel = event.channel; const newState = event.newState; // Update UI with new state information this.updateBalanceDisplay(channel.getID(), newState.availableAssets); this.updateSpendableDisplay(channel.getID(), newState.spendableAssets); }
5.6.6. Best Practices
- Responsive Synchronization: Trigger synchronization in response to user actions that will use channel data
- Avoid Over-Synchronization: Don’t synchronize on every UI update; rely on the automatic process and only manually sync before critical operations
- Graceful Degradation: Handle failed synchronization attempts gracefully, possibly by retrying once and then displaying appropriate error messages
- UI Feedback: Show synchronization status to users during critical operations to set expectations around timing
- Cached Data Awareness: Clearly distinguish in the UI between data known to be current and potentially stale data
By applying these synchronization strategies, UI dApps can ensure they operate with the most up-to-date Token Pool state while minimizing unnecessary network traffic and providing a responsive user experience.
6. Under the Hood: GridScript and VM Meta-Data Protocol
While UI dApp developers primarily interact with the high-level CStateLessChannelsManager
and CStateLessChannel
JavaScript APIs, these APIs act as wrappers around lower-level system interactions involving #GridScript commands, the VM Meta-Data exchange protocol, and the Decentralized File System (DFS). Understanding this underlying flow provides deeper insight into the system’s operation.
6.1. Mapping JS API to GridScript
Many key operations initiated through the JavaScript API translate directly into specific #GridScript commands executed by GRIDNET Core nodes on the DSM. The CVMContext
and CStateLessChannelsManager
handle the formulation and dispatch of these commands.
-
CStateLessChannel.cashOut(token, awardeeID)
->xTTEx
command:- When a dApp calls
cashOut()
, theCStateLessChannel
object (often via the Manager) prepares the necessary parameters (the BER-encodedCTransmissionToken
data and theawardeeID
). - These parameters are pushed onto the #GridScript stack.
- The
xTTEx
command is invoked within a transaction context (BT
/CT
). - The Core node executes
xTTEx
, which performs the on-chain validation, updates the Token Pool state on DFS, and adjusts the awardee’s balance on the DSM. - User-friendly wrapper: The
xtt
command in the Terminal Interface acts as an inline parameter wrapper forxTTEx
.
- When a dApp calls
-
CStateLessChannel.synchronize()
->getPoolEx
command (viagetPool
wrapper):- Calling
synchronize()
triggers a request to fetch the latest Token Pool data. - Internally, this results in the
getPoolEx
#GridScript command being executed on a Core node. The Token Pool ID is pushed onto the stack first. getPoolEx
reads the corresponding.pool
file from DFS.- The result (the BER-encoded pool data or an error/null indicator) is packaged into a VM Meta-Data message and sent back to the originating
CVMContext
. - User-friendly wrapper: The
getPool
command in the Terminal Interface acts as an inline parameter wrapper forgetPoolEx
.
- Calling
-
Mobile App QR Intent for Pool Creation ->
genPoolEx
command:- The process initiated by a UI dApp to create a pool results in a QR Intent containing specific VM Meta-Data for the Mobile App.
- The Mobile App securely generates the (disarmed) Token Pool data.
- This data is returned to the Core Node serving the user’s session.
- To register this pool on-chain, a transaction containing the
genPoolEx
command is ultimately created. The necessary parameters (owner ID, receipt ID, pool parameters, disarmed pool data) are pushed onto the stack beforegenPoolEx
is called. genPoolEx
validates the parameters and writes the pool data to the appropriate DFS location.- User-friendly wrapper: The
genpool
command handles parsing inline parameters and invokinggenPoolEx
.
-
Parameter Passing Mechanisms:
-
Stack-based:
Core #GridScript functions (often suffixed with
Ex
) typically expect parameters to be pushed onto the data stack in a specific order before they are invoked, similar to traditional Forth. The JavaScript APIs handle this internally. -
Inline Parameters:
For user convenience in the Terminal Interface, many commands have wrappers (like
xtt
,getPool
,genpool
) that accept parameters directly on the command line (e.g.,xtt -t <token> -r <rewardee>
). These wrappers parse the inline arguments and push them correctly onto the stack before calling theirEx
counterparts.
-
6.2. VM Meta-Data (eVMMetaSectionType.stateLessChannels
)
The VM Meta-Data protocol is the primary channel for structured communication between the CVMContext
(representing the UI environment) and the Core Nodes. State-Less Channels have a dedicated section type.
-
Structure of
eVMMetaEntryType.StateLessChannelsElement
:When a Core Node needs to send channel-related data to the UI (e.g., an incoming TT, a response to
getPoolEx
), it packages it within a VM Meta-Data message using:-
Section Type:
eVMMetaSectionType.stateLessChannels
-
Entry Type:
eVMMetaEntryType.StateLessChannelsElement
-
Entry Data Fields:
Typically contains:
elementType
(eStateLessChannelsElementType
enum): Specifies the type of payload.name
(ArrayBuffer/String): Often the Token Pool ID or a related identifier.data
(ArrayBuffer): The BER-encoded payload (e.g., theCTransmissionToken
orCTokenPool
data).
-
-
Element Types (
eStateLessChannelsElementType
):The
elementType
field distinguishes the payload:token
: Contains BER-encodedCTransmissionToken
data representing a payment/reward.tokenPool
: Contains BER-encodedCTokenPool
data (usually a response togetPoolEx
).transitPool
: Conceptual. While defined, direct transmission of an aggregated “Transit Pool” structure is less common; cash-out typically uses the latesttoken
. Events related to successful cash-out might use this type orreceipt
.receipt
: Contains information about a finalized on-chain transaction related to a channel operation (e.g., a cash-out receipt).bankUpdate
: Contains BER-encodedCTransmissionToken
data specifically flagged as a bank update from the owner (via Mobile App), used to reveal more spendable assets for an Outgress channel.
-
How
CVMContext
Routes Data:CVMContext
receives a VM Meta-Data message from a Core Node.- It parses the message and identifies sections/entries.
- If it finds a section of type
stateLessChannels
, it iterates through itsStateLessChannelsElement
entries. - It dispatches a generic
newVMMetaData
event containing the rawmetaMsg
. - The
CStateLessChannelsManager
, subscribed to this event vianewVMMetaDataCallback
, receives themetaMsg
. - The Manager specifically looks for the
stateLessChannels
section, extracts theelementType
and BER-encodeddata
from relevant entries. - It deserializes the
data
into the appropriate JavaScript object (CTokenPool
,CTransmissionToken
). - It then processes this object internally (e.g., finds the target
CStateLessChannel
) and raises the specific, higher-level API events (e.g.,onTT
,onNewTokenPool
) for subscribed UI dApps.
6.3. Decentralized File System (DFS) Interaction
The DFS provides persistent, replicated storage essential for the State-Less Channels system.
-
Storing Token Pools:
The public data of a Multi-Dimensional Token Pool, once generated and committed via
genPoolEx
, is stored as a regular file within the owner’s State Domain on the DFS. Conventionally, these files have a.pool
extension and reside in aTokenPools
subdirectory (e.g.,/OwnerID/TokenPools/PoolID.pool
). -
Discovery (
doCD
):The
CStateLessChannelsManager
uses thedoCD
DFS command (sent viaCVMContext
) to list the contents of an owner’s/TokenPools/
directory. This is how it discovers the IDs (filenames) of pools belonging to a specific agent during thesyncAgentChannels
process. -
Retrieval (
doGetFile
):Once a pool’s filename (ID) is known (either from discovery or because the dApp already knew it), the Manager or
CStateLessChannel
triggers adoGetFile
DFS command (viaCVMContext
) to retrieve the file’s content. -
How
CVMContext
Routes DFS Data:CVMContext
receives a DFS message response (e.g., directory listing fromdoCD
, file content fromdoGetFile
) from a Core Node.- It dispatches a generic
newDFSMsg
event containing thedfsMsg
object. Note that DFS file content responses often embed the content within a VM Meta-Data structure inside the DFS message’sData1
field. - The
CStateLessChannelsManager
, subscribed vianewDFSMsgCallback
, receives thedfsMsg
. - It checks if the request ID matches one it initiated (
hasDFSRequestID
). - It parses the message type. If it’s a directory listing, it might queue
doGetFile
requests. If it’s file content (eDFSElementType.fileContent
), it parses the embedded VM Meta-Data, extracts the BER-encodedCTokenPool
data from the appropriate field. - It deserializes the pool data.
- It calls its internal
onTokenPoolReceived
method, which finds/updates the relevantCStateLessChannel
and raises events for UI dApps.
7. Practical Usage & Examples (UI dApp Perspective)
This section provides practical code examples and outlines common scenarios for UI dApps interacting with the GRIDNET OS State-Less Channels system via the CVMContext
and CStateLessChannelsManager
. These examples assume they are running within the context of a class extending CWindow
, where this.mVMContext
refers to the CVMContext
singleton and this.mID
is the dApp’s process ID.
7.1. Setup: Getting the Manager, Subscribing to Events
Before interacting with channels, your dApp needs access to the manager and should subscribe to relevant events within its initialization logic (e.g., in the constructor or an initialize
method).
class MyDApp extends CWindow { constructor( /* ... CWindow params */ ) { super( /* ... CWindow params */ ); this.mVMContext = CVMContext.getInstance(); this.mChannelsManager = this.mVMContext.getChannelsManager; this.myChannelListeners = []; // Keep track of listener IDs for cleanup this.initializeEventListeners(); } initializeEventListeners() { // Subscribe to new incoming Transmission Tokens let listenerIdTT = this.mChannelsManager.addNewTTListener(this.handleNewTT.bind(this), this.mID); this.myChannelListeners.push(listenerIdTT); // Subscribe to channel state changes (e.g., balance updates after sync/TT validation) let listenerIdState = this.mChannelsManager.addStateChannelNewStateListener(this.handleChannelStateChange.bind(this), this.mID); this.myChannelListeners.push(listenerIdState); // Subscribe to cash-out confirmations (optional but recommended) let listenerIdReceipt = this.mChannelsManager.addNewReceiptListener(this.handleReceipt.bind(this), this.mID); this.myChannelListeners.push(listenerIdReceipt); // Subscribe to newly managed channels (optional, e.g., for dynamic list updates) let listenerIdNewChannel = this.mChannelsManager.addNewStateChannelListener(this.handleNewChannel.bind(this), this.mID); this.myChannelListeners.push(listenerIdNewChannel); } // --- Callback Handlers (defined later) --- async handleNewTT(event) { /* ... */ } async handleChannelStateChange(event) { /* ... */ } async handleReceipt(event) { /* ... */ } async handleNewChannel(event) { /* ... */ } closeWindow() { // IMPORTANT: Unregister listeners when the dApp closes to prevent memory leaks this.myChannelListeners.forEach(listenerId => { this.mVMContext.unregisterEventListenerByID(listenerId); // Use CVMContext to unregister }); this.myChannelListeners = []; super.closeWindow(); // Call parent class method } // ... other dApp methods ... }
7.2. Use Case 1: Receiving Real-time Micropayments (e.g., Game Rewards)
Your dApp needs to react when a new off-chain payment arrives.
// Inside MyDApp class async handleNewTT(event) { const token = event.token; const isBankUpdate = event.isBankUpdate; const poolIdStr = gTools.encodeBase58Check(token.getTokenPoolID()); console.log(`Received TT for pool: ${poolIdStr}, Is Bank Update: ${isBankUpdate}`); if (isBankUpdate) { // This is likely for an Outgress channel owned by the user, // updating the spendable balance. React accordingly if needed. console.log("Bank update received, potentially more funds available to send."); return; } // It's a potential payment/reward for an Ingress channel const channel = this.mChannelsManager.findChannelByID(token.getTokenPoolID()); if (!channel) { console.warn(`Received TT for unknown channel ${poolIdStr}. Manager will attempt to sync.`); // The manager might automatically create the channel object later // after fetching pool data via DFS triggered by the unknown TT. return; } if (channel.getIsOutgress) { console.log(`Ignoring TT for Outgress channel ${poolIdStr} (likely sent by this user).`); return; // Usually not interested in tokens you generated yourself } // --- Validate without immediately updating the channel's core state --- // This tells you the value of *this specific token*, assuming it's valid // relative to the *currently cached* pool state. const newlyReceivedValue = await channel.validateTT(token, false); // false = don't update state yet if (newlyReceivedValue > 0n) { console.log(`Valid TT received representing ${newlyReceivedValue} Atto`); // --- Now update the channel state with this valid token --- // This marks the token as 'seen' locally for accumulation calculation await channel.validateTT(token, true); // true = update cached state channel.setRecentToken = token; // Store the latest valid token // --- Get the total potential cash-out value based on the *latest* token --- const accumulatedValue = await channel.getAccumulatedValue(); console.log(`Total accumulated off-chain value for channel ${channel.getFriendlyID || poolIdStr}: ${accumulatedValue} Atto`); // --- Update your UI --- this.updateMyGameScoreDisplay(poolIdStr, accumulatedValue); // Example UI update function } else if (newlyReceivedValue === 0n) { console.warn(`Received TT for channel ${poolIdStr} is invalid or a replay.`); // E.g., Double spend attempt, or hash chain mismatch. Ignore it. } else { // -1n console.warn(`Cannot validate TT for channel ${poolIdStr} yet, pool data not ready.`); // Need to wait for channel.synchronize() to complete. } } updateMyGameScoreDisplay(channelId, accumulatedValue) { // Find the UI element representing this channel/score and update it // Example: document.getElementById(`score_${channelId}`).innerText = `${gTools.attoToGNCStr(accumulatedValue)} GNC`; console.log(`UI Update: Channel ${channelId} accumulated ${gTools.attoToGNCStr(accumulatedValue)} GNC`); }
7.3. Use Case 2: Cashing Out Received Tokens
When the user wants to convert their accumulated off-chain rewards into on-chain balance.
// Inside MyDApp class async cashOutChannel(channelId) { const channel = this.mChannelsManager.findChannelByID(channelId); if (!channel) { this.showError("Channel not found."); return; } if (channel.getIsOutgress) { this.showError("Cannot cash out from an Outgress channel."); return; } const latestToken = channel.getRecentToken; if (!latestToken) { this.showError("No valid tokens received to cash out for this channel yet."); return; } const accumulatedValue = await channel.getAccumulatedValue(); if (accumulatedValue <= 0n) { this.showError("Accumulated value is zero, nothing to cash out."); return; } const myDomainID = this.mVMContext.getUserID(); // Or getUserFullID() if (!myDomainID || myDomainID.length === 0) { this.showError("Cannot cash out: User ID not available. Please log in."); // Trigger login flow if necessary // this.mVMContext.requestQRLogon(); return; } console.log(`Attempting to cash out ${accumulatedValue} Atto from channel ${channel.getFriendlyID || channelId} to ${myDomainID}`); this.showLoadingIndicator(`Cashing out ${gTools.attoToGNCStr(accumulatedValue)} GNC...`); // Initiate the cash-out process const requestSent = channel.cashOut(latestToken, myDomainID, false); // false = don't auto-commit yet if (!requestSent) { this.hideLoadingIndicator(); this.showError("Failed to send cash-out request to the network."); } else { console.log("Cash-out request queued. Waiting for confirmation via receipt listener or commit."); // The actual commit might happen later via the Magic Button or another dApp action. // The handleReceipt callback will be triggered upon confirmation. } } // Callback for transaction receipts async handleReceipt(event) { const receipt = event.receipt; // TODO: Need logic to correlate this receipt specifically to our cash-out transaction. // This might involve checking TX type, involved parties, or matching against // a TX ID stored when cashOut() was called (if the API provided it). // For now, assume any relevant receipt means success/failure. console.log("Received a transaction receipt:", receipt); this.hideLoadingIndicator(); if (receipt.status === 'success') { // Assuming receipt structure this.showMessage("Cash-out successful!"); // Optionally, re-sync the channel state or balance after confirmation // this.mVMContext.retrieveBalance(true); } else { this.showError(`Cash-out failed: ${receipt.errorMessage || 'Unknown error'}`); } }
7.4. Use Case 3: Creating a Token Pool (e.g., for a Game Server/Service)
Creating a pool requires an on-chain sacrificial transaction and secure generation using the MasterSeedHash, typically involving the mobile app.
// Inside MyDApp class (e.g., an admin panel dApp) async createTokenPool(totalValueGNC, numDimensions, friendlyId = "") { const totalValueAtto = gTools.GNCToAtto(totalValueGNC); // Convert GNC to Atto // 1. Perform the Sacrificial Transaction (Requires separate logic/UI) // This would involve using CVMContext.processGridScript to send GNC // to a specific system address or using a dedicated 'sacrifice' command. // You need the receipt ID of this transaction. const sacrificialReceiptId = await this.performSacrifice(totalValueAtto); // Placeholder function if (!sacrificialReceiptId) { this.showError("Sacrificial transaction failed."); return; } console.log(`Sacrifice successful, Receipt ID: ${sacrificialReceiptId}`); // 2. Prepare the QR Intent for Mobile App Generation const ephKeyPair = this.mVMContext.getKeyPair; const myOwnerId = this.mVMContext.getUserFullID(); // Use full ID for ownership const valuePerToken = 1n; // Example: 1 Atto per token // Create a *template* CTokenPool object (most values are placeholders here) // The mobile app calculates depths and generates hashes based on parameters const poolTemplate = new CTokenPool( null, // CryptoFactory not needed here numDimensions, myOwnerId, sacrificialReceiptId, // Link to the sacrifice valuePerToken, totalValueAtto, 0n, // CurrentIndex (starts at 0) friendlyId // Seed/Final/Current hashes are generated by mobile app ); const metaGen = new CVMMetaGenerator(); // The addGenTokenPoolRequest prepares the specific VM Meta-Data structure // that tells the mobile app what parameters to use for generation. metaGen.addGenTokenPoolRequest(poolTemplate); const qr = new CQRIntent( eQRIntentType.QRProcessVMMetaData, // Instructs mobile app to process the metadata this.mVMContext.getConversationID(), // Target this web session this.mVMContext.getFullNodeIP(), // Route through this node eEndpointType.WebSockConversation ); qr.setPubKey(ephKeyPair.public); qr.setData(metaGen.getPackedData()); // Embed the generation request // Display the QR code to the user this.showQRCode(qr); // Placeholder UI function to display the QR object console.log("Please scan the QR code with your GRIDNET Token app to generate the Token Pool securely."); // 3. Wait for the Response // The mobile app will generate the pool, send the disarmed data back to the Core node, // which sends it via VM Meta-Data to CVMContext -> ChannelsManager. // Your dApp will be notified via the addNewTokenPoolListener / addNewStateChannelListener. // The dApp then needs to initiate the *second* transaction (BT... genPoolEx ... CT) // using the received disarmed pool data to register it on-chain. }
7.5. Use Case 4: Sending Off-Chain Rewards/Payments (Outgress Channel)
Using a pool you own to send value to another peer off-chain.
// Inside MyDApp class async sendOffChainPayment(poolId, recipientId, valueAtto) { const channel = this.mChannelsManager.findChannelByID(poolId); if (!channel) { this.showError(`Channel ${poolId} not found or managed.`); return; } if (!channel.getIsOutgress) { this.showError(`Channel ${poolId} is not an Outgress channel you own.`); return; } if (!channel.getIsReadyForVerification) { this.showError(`Channel ${poolId} is not synchronized. Please sync first.`); // await channel.synchronize(); // Optionally trigger sync return; } console.log(`Attempting to generate TT for ${valueAtto} Atto to ${recipientId} from pool ${poolId}`); // Use getTTForPeer for automatic bank management const transmissionToken = await channel.getTTForPeer(valueAtto, recipientId, true); // true = mark as used locally if (!transmissionToken) { const spendable = await channel.getLocallySpendableAssets(); this.showError(`Failed to generate TT. Insufficient funds in available banks or no free banks. Locally Spendable: ${spendable} Atto`); // May need a bank update from mobile app if spendable is low but total valueLeft is high return; } console.log("Transmission Token generated successfully:", transmissionToken); // --- Deliver the Transmission Token --- // This part is application-specific and depends on how you communicate with the recipient. // Examples: // 1. Send via WebRTC Swarm: // const swarmId = this.mVMContext.getMainSwarmID(); // Or another specific swarm // const success = this.mVMContext.getSwarmsManager.sendData( // transmissionToken.getPackedData(), // recipientId, // Target the specific peer // swarmId // ); // if (success) { console.log("TT sent via swarm."); } else { console.error("Failed to send TT via swarm."); } // 2. Send via direct P2P connection (if available) // 3. Send via a centralized intermediary (if applicable to your dApp) this.showSuccess(`Transmission Token for ${valueAtto} Atto generated. Delivery mechanism required.`); }
7.6. UI Updates based on Channel State
Keep the UI synchronized with the latest known channel state.
// Inside MyDApp class async handleChannelStateChange(event) { const channel = event.channel; const newState = event.newState; // const previousState = event.previousState; // Can be used for comparison if needed const poolIdStr = gTools.encodeBase58Check(channel.getID()); const friendlyId = channel.getFriendlyID() || poolIdStr; console.log(`State change detected for channel: ${friendlyId}`); // Update UI elements associated with this channel this.updateChannelDisplay(poolIdStr, { accumulated: newState.accumulatedAssets, // For ingress channels (potential cash-out value) spendable: newState.spendableAssets, // For outgress channels (what can be sent now) availableOnChain: newState.availableAssets // Total value left according to last sync/update }); } updateChannelDisplay(channelId, stateData) { // Example: Find UI elements and update their content const elemAccumulated = document.getElementById(`channel_${channelId}_accumulated`); const elemSpendable = document.getElementById(`channel_${channelId}_spendable`); const elemAvailable = document.getElementById(`channel_${channelId}_available`); if (elemAccumulated) elemAccumulated.innerText = `${gTools.attoToGNCStr(stateData.accumulated)} GNC`; if (elemSpendable) elemSpendable.innerText = `${gTools.attoToGNCStr(stateData.spendable)} GNC`; if (elemAvailable) elemAvailable.innerText = `${gTools.attoToGNCStr(stateData.availableOnChain)} GNC`; }
8. Advanced Topics
This section explores some of the more intricate aspects of the State-Less Channels system, including its underlying security model, error handling, performance characteristics, and security considerations.
8.1. Sybil-Proofing Mechanics
A fundamental requirement for any decentralized incentivization system operating in an open network is resistance to Sybil attacks, where a single malicious entity creates numerous fake identities to gain disproportionate influence or rewards.
The GRIDNET OS State-Less Channels system achieves Sybil-proofing primarily through the design of its
reward function
and the inherent properties of the
path assurance protocols
(PA1, PA2, PA3) used for data propagation, as detailed comprehensively in our research [1].
-
Key Principles:
-
Investment-Based Rewards:
The core reward mechanism ties potential earnings to the verifiable “stake” or investment represented by the value and depth of the Token Pools involved (specifically the
ABI
value used in the identity rating functione
). Creating numerous low-value Sybil identities to participate in a path yields significantly lower expected rewards compared to using a single, well-funded identity. -
Path Integrity:
The path assurance mechanisms ensure that the sequence of intermediaries recorded in the Transmission Token’s journey (implicitly or explicitly depending on the protocol variant) cannot be easily tampered with without invalidating cryptographic checks performed during the on-chain cash-out (
xTTEx
). -
Reward Function Design:
The specific mathematical formulation of the reward function
FA
ensures that concentrating investment into a single identity for a given path maximizes potential reward compared to distributing the same total investment across multiple Sybil identities within that same path.
-
-
In Essence:
While the system cannot prevent the creation of Sybil identities at the network level, it makes using them within the incentivized State-Less Channel data flows economically irrational for profit-maximizing agents.
-
Further Reading:
For a rigorous mathematical proof and detailed breakdown of the algorithms and game-theoretical analysis, please refer to Section 7 (“Proof of sybil-proofness”) and Section 6 (“Reward function”, “Routing mechanism”) of the SPIDE research paper [1].
8.2. Error Handling
Developers should anticipate potential issues when working with off-chain systems and network interactions.
-
Invalid Transmission Tokens:
-
Cause:
Malformed data, failed hash chain verification (indicating tampering or an incorrect sequence), invalid signature (on authenticated TTs).
-
Detection:
The
CStateLessChannel.validateTT()
method will return0n
. -
Handling:
The receiving UI dApp should simply discard or ignore invalid tokens. Attempting to cash them out via
xTTEx
will fail on-chain.
-
-
Insufficient Funds:
-
Sending (Outgress):
channel.getTTForPeer()
orchannel.getTTWorthValue()
will returnnull
. The dApp should check the return value and inform the user. They might need a “Bank Update” from their mobile app if sufficient value exists in the pool overall but not in currently pre-cached banks (channel.getLocallySpendableAssets()
vs.channel.getTokenPool().getValueLeft()
). -
Cashing Out (Ingress):
If a recipient tries to cash out a token representing more value than currently available in the bank on-chain (e.g., due to a race condition or delay in state update), the
xTTEx
command will fail during DSM execution.
-
-
Double Spends / Replays:
-
Off-Chain:
channel.validateTT()
will return0n
if the incoming token’sbankUsageDepth
is less than or equal to the currently cached depth for that bank. -
On-Chain:
The
xTTEx
command inherently prevents double-spending by verifying the token’sbankUsageDepth
against the authoritative state stored on the DSM. Any attempt to cash out a token representing already spent depth will be rejected by consensus.
-
-
Network Issues & Timeouts:
- Requests sent via
CVMContext
(e.g., forsynchronize()
orcashOut()
) involve network communication and may fail or time out. -
Handling:
JavaScript Promises returned by async API calls will reject. Methods returning simple success indicators (like
synchronize()
) will returnfalse
. UI dApps must implement standard error handling (try...catch
for async/await, checking return values) and potentially implement retry logic or inform the user.
- Requests sent via
-
Synchronization Issues (Stale Cache):
-
Cause:
The local
CTokenPool
cache within aCStateLessChannel
might not reflect the absolute latest on-chain state if a cash-out occurred recently but the channel hasn’t been synced. -
Effect:
validateTT()
might incorrectly validate a token that would be rejected on-chain, orgetAccumulatedValue()
might report a higher value than actually cashable.getTTForPeer()
might fail even if funds were available on-chain. -
Mitigation:
Use
getIsReadyForVerification
to ensure initial data is loaded. Callchannel.synchronize()
periodically or before critical operations, especially cash-outs, if high accuracy is required. TheCStateLessChannelsManager
performs periodic background synchronization.
-
8.3. Performance and Scalability Considerations
The State-Less Channels system is designed for high performance and scalability compared to pure on-chain transactions.
-
Off-Chain Speed:
Transmission Token validation (
validateTT
) and generation (getTT...
) are purely local cryptographic operations (primarily hashing) and are extremely fast. The main latency is network delivery time between peers. -
On-Chain Bottlenecks:
The primary performance limitations are the frequency and cost of on-chain operations:
- Token Pool Creation (
genPoolEx
): A one-time cost per pool, involving writing data to DFS. - Cashing Out (
xTTEx
): Requires an on-chain transaction, consuming ERG and subject to DSM throughput (TPS).
- Token Pool Creation (
-
Optimizations:
-
Batching Cash-Outs:
Recipients are incentivized to delay cashing out until a significant value is accumulated or the channel dimension is nearly depleted, reducing the frequency of on-chain transactions.
-
Multi-Dimensionality:
Allows many parallel off-chain streams using a single pool, significantly increasing the effective off-chain TPS supported by a single on-chain pool registration.
-
State-Less Design:
Reduces the need for constant peer-to-peer state synchronization messages compared to some other channel designs.
-
-
Developer Considerations:
Design dApps to minimize unnecessary cash-outs. Choose an appropriate number of dimensions for M-DTPs based on expected concurrent interactions. Rely on the Manager’s autonomous features where possible.
8.4. Security
Security relies on a combination of cryptographic primitives, consensus rules, and operational security practices.
-
Mobile App Trust:
The GRIDNET Token mobile app is the primary custodian of the user’s private keys and the critical MasterSeedHash for owned Token Pools. Its security is paramount. Compromise of the mobile app could lead to the loss of funds secured by M-DTPs generated from its MasterSeedHash.
-
QR Intent Security:
While QR codes themselves are just data, the process involving them is designed for security. Sensitive operations initiated via QR require confirmation and cryptographic signing within the secure mobile app environment after the QR is scanned. The response from the mobile app back to the Core node is typically onion-routed and end-to-end encrypted.
-
Signature Verification:
- Authenticated TTs: Provide non-repudiation and proof of origin from the pool owner. The signature is verified against the pool’s public key (if available/published).
xTTEx
on-chain performs this verification if a signature is present. - Unauthenticated TTs: Rely solely on the hash chain math and the DSM’s state tracking for validity regarding value and double-spending. They do not cryptographically prove origin from the pool owner.
- Authenticated TTs: Provide non-repudiation and proof of origin from the pool owner. The signature is verified against the pool’s public key (if available/published).
-
Hash Chain Integrity:
The security against fabricating tokens relies on the pre-image resistance and collision resistance of the underlying cryptographic hash function (e.g., SHA256).
-
DSM Consensus:
The ultimate security against on-chain double-spending and invalid state transitions relies on the integrity and Sybil-resistance of the underlying GRIDNET OS consensus mechanism.
-
Full Node Honesty:
For unauthenticated TTs, there’s a theoretical risk: if a malicious node (e.g., the one serving the recipient’s session) intercepts an unauthenticated TT before the legitimate recipient caches it out, and that node knows the recipient’s ID, it could attempt to cash it out to itself first. Using authenticated TTs or ensuring prompt cash-out by the recipient mitigates this specific, more complex attack vector. Standard TTs used for payments between distinct parties are usually unauthenticated for efficiency. Bank Updates must be authenticated.
9. Appendices
9.1. Glossary
-
Atto:
The smallest indivisible unit of GRIDNET’s native currency (GBU). Used for precise value representation in Token Pools.
-
Bank (Dimension):
An independent hash chain within a Multi-Dimensional Token Pool, used to manage a distinct off-chain payment stream.
-
Cash Out:
The on-chain process of redeeming the value represented by off-chain Transmission Tokens, resulting in an update to the recipient’s on-chain GBU balance and the Token Pool’s state on the DSM.
-
Ceiling Hash (Final Hash):
The final, publicly known hash value anchoring a specific Bank’s hash chain within a Token Pool, stored on the DSM.
-
CStateLessChannel:
The JavaScript class representing a single State-Less Channel instance within the UI dApp environment, managing interaction with a specific Token Pool.
-
CStateLessChannelsManager:
The JavaScript singleton class managing all State-Less Channel instances, handling events, synchronization, and autonomous operations within the UI dApp environment.
-
CVMContext:
The central JavaScript singleton acting as the gateway between UI dApps and the underlying GRIDNET OS functionalities.
-
DFS (Decentralized File System):
GRIDNET OS’s integrated, decentralized storage layer where Token Pool data files are stored.
-
DPT (Decentralized Processing Thread):
An execution context within GRIDNET OS, associated with an IVR, responsible for running code (#GridScript).
-
DSM (Decentralized State Machine):
The core consensus-driven engine of GRIDNET OS, responsible for processing transactions and maintaining the global state, including Token Pool data stored on DFS.
-
GBU (GRIDNET Basic Unit):
The primary native currency/asset of the GRIDNET OS ecosystem.
-
GridScript:
The specialized, Forth-like programming language used by GRIDNET Core nodes and DPTs to execute instructions and interact with the DSM.
-
IVR (Isolated VM Realm):
A sandboxed execution environment associated with a DPT, operating on a copy of the System Trie (Private VM Realm) or directly affecting the main state (Global VM Realm in Commitment Mode).
-
M-DTP (Multi-Dimensional Token Pool):
The core data structure for State-Less Channels, registered on-chain, containing multiple independent hash chains (Banks/Dimensions) derived from a single MasterSeedHash, representing a pre-funded store of value for off-chain payments.
-
MasterSeedHash:
The high-entropy secret held by the Token Pool owner (usually in the Mobile App), used to generate all hashes (tokens) within all dimensions of an M-DTP.
Must be kept secret.
-
Receipt ID:
The unique identifier of an on-chain transaction (e.g., the sacrificial transaction used to fund a Token Pool).
-
Sacrificial Transaction:
An on-chain transaction where the sender locks value, the receipt of which is then used to authorize the creation of a Token Pool of equivalent value.
-
SeedHash (Dimension Seed Hash):
The starting secret hash for a specific Bank/Dimension’s hash chain, deterministically derived from the MasterSeedHash.
-
State-Less Channel:
The logical off-chain payment channel facilitated by an M-DTP and the exchange of TTs, minimizing peer-to-peer state synchronization requirements during the off-chain phase.
-
TT (Transmission Token):
The data structure (
CTransmissionToken
object) exchanged off-chain, representing a specific amount of value released from a specific Bank of an M-DTP. It contains the revealed hash and updated bank state information needed for verification and eventual cash-out. -
Transit Pool:
A conceptual term referring to the accumulated off-chain value represented by a sequence of TTs for a single bank since the last on-chain cash-out. Usually, only the latest TT is needed to represent the entire Transit Pool when cashing out.
-
Usage Depth:
An integer value stored on-chain for each Bank, indicating how many tokens (hashes) have been successfully revealed and cashed out from that Bank’s hash chain. This prevents double-spending.
9.2. Enum Definitions (JavaScript)
These enums define constants used within the State-Less Channels API:
-
eChannelDirection
: Defines the perspective of a channel relative to the local user.
ingress
: The local user is the recipient of funds via this channel.outgress
: The local user owns the pool and sends funds via this channel.both
: Used for filtering, not typically assigned to a channel instance.
-
eTokenPoolStatus
: Overall status of a Multi-Dimensional Token Pool.
active
: The pool is operational and has spendable value.depleted
: All dimensions/banks within the pool have been fully cashed out on-chain.banned
: The pool has been marked as invalid or banned by the system (e.g., due to detected fraud).
-
eTokenPoolBankStatus
: Status of an individual dimension/bank within a Token Pool.
active
: The bank has unspent tokens.depleted
: All tokens in this specific bank have been cashed out on-chain.
-
eStateLessChannelsElementType
: Used within the VM Meta-Data protocol to identify the type of channel-related payload.
token
: Payload is aCTransmissionToken
representing a standard payment/reward.tokenPool
: Payload is aCTokenPool
object’s public data.transitPool
: (See Glossary/API clarification).receipt
: Payload contains details of an on-chain transaction receipt related to a channel operation.bankUpdate
: Payload is aCTransmissionToken
specifically flagged as revealing pre-cached hashes for an Outgress channel.
-
eChannelLocalPingType
: Used internally by
CStateLessChannel.ping()
to specify which timestamp to update.update
: General update timestamp.lastUsed
: Timestamp for when a TT was generated from the channel.lastSync
: Timestamp for whensynchronize()
was last initiated.lastCashOut
: Timestamp for whencashOut()
was last initiated.
9.3. Relevant GridScript Commands
These are the low-level commands executed by the DSM, typically invoked via the JavaScript API wrappers:
-
genPoolEx
:(Requires parameters on stack: OwnerID, ReceiptID, DimensionsCount, TokenValue, FriendlyID, DisarmedPoolData) Registers a new, pre-generated (disarmed) Multi-Dimensional Token Pool on the DFS, validating it against the provided sacrificial transaction receipt.
- Wrapper:
genpool
(accepts inline parameters)
- Wrapper:
-
xTTEx
:(Requires parameters on stack: BER-encoded Transmission Token, optional RewardeeID) Processes an off-chain Transmission Token for on-chain cash-out. Verifies the token against the Token Pool state on DFS, updates the bank’s usage depth, and credits the rewardee’s on-chain balance.
- Wrapper:
xtt
(accepts inline parameters)
- Wrapper:
-
getPoolEx
:(Requires TokenPool ID on stack) Retrieves the BER-encoded public data of a specified Token Pool from the DFS.
- Wrapper:
getPool
(accepts inline parameters)
- Wrapper:
9.4. Links
-
Research Papers:
- Skowroński, R.; Brzeziński, J. SPIDE: Sybil-proof, incentivized data exchange. Clust. Comput.
2022
, 25, 2241–2270. DOI:10.1007/s10586-021-03411-3. Available online: https://www.researchgate.net/publication/356419641_SPIDE_sybil-proof_incentivized_data_exchange (accessed [Date])
- Skowroński, R.; Brzeziński, J. UI dApps Meet Decentralized Operating Systems. Electronics
2022
, 11, 3004. DOI:10.3390/electronics11193004. Available online: https://www.mdpi.com/2079-9292/11/19/3004 (accessed [Date])
- Skowroński, R.; Brzeziński, J. SPIDE: Sybil-proof, incentivized data exchange. Clust. Comput.
-
Code Examples:
-
Wallet UI dApp:
[Link to GRIDNET OS GitHub Repository – Wallet dApp source] (Requires specific link once available) – Demonstrates receiving tokens, displaying accumulated value, and initiating cash-outs.
-
GridCraft – Illustrates how Transmission Tokens can be used to reward players for collecting dynamic Gold Veins in the ground.
-
-
Tutorials:
-
GRIDNET OS Magazine – #GridScript Basics and Micropayments:
https://gridnet.org/wpp/index.php/2021/06/11/gridscript-basics-and-micropayments-from-the-users-perspective/ (Provides practical Terminal examples related to pool creation and cashing out).
-
GRIDNET OS UI dApp Development Tutorial:
[Link to Tutorial] (Placeholder – A comprehensive tutorial covering dApp setup, CVMContext usage, and potentially basic channel interaction).
-
10. GRIDNET OS State-Less Channels Security Threat Model
This comprehensive security threat model analyzes the GRIDNET OS State-Less Channels system, focusing on its security architecture, potential attack vectors, and defensive mechanisms. The system employs multiple layers of security through cryptographic primitives, compartmentalization, and verification protocols to protect off-chain transactions.
10.1. Trust Boundaries & Security Zones
As illustrated in the first diagram, the system is divided into three distinct security zones:
High Security Zone (Mobile App)
- Contains the most sensitive cryptographic material: MasterSeedHash and Dimension Seeds
- Hardware-backed secure storage for private keys
- Limited interface with other zones through controlled channels (QR Intents)
Medium Security Zone (Browser/UI dApp)
- Receives only the necessary subset of hash tokens via Bank Updates
- Maintains local cache of pool state (public metadata and limited revealed hashes)
- Shadow DOM isolation for UI components
Public Zone (Blockchain/DSM)
- Only contains fully public data: ceiling hashes, bank usage depths, pool metadata
- Provides consensus validation for cash-out operations
The security architecture enforces strict information flow between zones, with higher security zones only revealing controlled subsets of data to lower security zones.
10.2. Reverse Hash Chain Security Model
A core security mechanism in the system is the reverse hash chain:
- During pool creation, a sequence of hashes is generated in one direction:
Hash_0 (Seed) → Hash_1 → Hash_2 → … → Hash_N (Final/Ceiling) - Only the Final Hash (Hash_N) is published publicly on the blockchain
- For spending, hashes are revealed in reverse order (N-1, N-2, etc.)
The security property that makes this work is the one-way nature of cryptographic hash functions: given Hash_i, it’s computationally infeasible to derive Hash_{i-1}. This means:
- Revealing Hash_i does NOT compromise Hash_{i-1}
- Early hashes in the chain maintain their secrecy even as later hashes are revealed
- Each revealed hash can be verified against the public ceiling hash
This enables secure, off-chain value transfer with on-chain anchoring.
10.3. Token Pool Compartmentalization
The system further enhances security through Multi-Dimensional Token Pools (M-DTPs):
- Each pool has multiple independent banks/dimensions
- Each bank has its own separate hash chain derived from the MasterSeedHash
- The mobile app controls precisely which hash segments from which banks are revealed
- UI dApp is only given access to specific hash segments needed for current operations
This compartmentalization ensures that:
- Compromise of one bank doesn’t affect others
- Only a limited subset of value is exposed to the medium-security browser environment
- The browser environment never has access to the MasterSeedHash or dimension seeds
- Risk is isolated to specific revealed hash segments
10.4. Attack Vectors & Threat Analysis

The threat model identifies five primary attack vectors:
Attack Vector 1: Mobile App Compromise
- Threat: Extraction of MasterSeedHash or dimension seeds
- Impact: Total compromise of all banks in affected token pools
- Mitigations: Hardware-backed secure storage, biometric protection, limited export capability
Attack Vector 2: Browser/UI dApp Compromise
- Threat: Extraction of revealed hashes from bank updates
- Impact: Limited to already-revealed tokens in specific banks
- Mitigations: Limited bank updates, shadow DOM isolation, request signing
Attack Vector 3: Replay Attacks
- Threat: Reuse of previously revealed TTs to claim value multiple times
- Impact: Double-spending of the same token value
- Mitigations: On-chain bank usage depth tracking, validation in xTTEx execution
Attack Vector 4: Man-in-the-Middle
- Threat: Intercepting TTs during off-chain transmission
- Impact: Unauthorized party could attempt to cash out tokens
- Mitigations: Optional TT recipient ID, authenticated TTs, secure transmission channels
Attack Vector 5: Cryptographic Attacks
- Threat: Finding hash function collisions or preimages
- Impact: Potential forgery of tokens
- Mitigations: Strong cryptographic hash functions with high preimage resistance
10.5. Defense-in-Depth Model
The State-Less Channels system implements defense-in-depth with four security layers:
Layer 1: Cryptographic Primitives
- One-way hash functions (SHA-256)
- Public key cryptography for signatures
- Reverse hash chains for secure revelation
Layer 2: Multi-Dimensional Structure
- Multiple independent banks isolate risk
- Limited bank depth exposure through controlled updates
- Compartmentalized token pools
Layer 3: Security Domain Separation
- Mobile app isolation of critical secrets
- Shadow DOM segregation in browser
- QR Intent protocol for secure cross-domain communication
Layer 4: Verification & Validation
- On-chain depth tracking prevents double-spending
- DSM consensus validation for all cash-outs
- Optional TT signatures for authentication
10.6. Conclusion
The GRIDNET OS State-Less Channels system demonstrates a sophisticated security architecture that:
- Compartmentalizes risk through multi-dimensional token pools
- Minimizes trusted computing base by keeping critical secrets in secure hardware
- Provides cryptographic guarantees through one-way hash functions
- Prevents double-spending through on-chain depth tracking
- Limits exposure by selective revelation of hash tokens
The core innovation of using reverse hash chains allows for secure off-chain value transfer while maintaining strong cryptographic guarantees about the security and integrity of the system, even if parts of it are compromised.