GRIDNET OS Data Request Flow Documentation

Table of Contents

  1. Overview
  2. Architecture Overview
  3. Local Data Request Flow
  4. Remote Data Request Flow
  5. Implementation Examples
  6. Edge Cases and Error Handling
  7. Best Practices

Overview

Sometimes internal components of the system, remote GRIDNET Core nodes or UI dApps need to request data from user.

GridScript comes with facilities directly requesting user input from user.

When accessing GRIDNET OS through SSH the qeustions would be printed on screen and data conversation (from string) taken care of automatically.

When accessing GRIDNET OS throgh Decentralized Terminal Services - either the particular UI dApp instance or the in-web-browser sub-system of GRIDNET OS itslef would be taking care of reqesting data from user (and taking care of appropriate data convesions).

In this article we focus on system-level (kernel mode) interactions between the Wallet UI dApp, the Key Chain Managment sub-system, the user and the rest of the system.

The Gist: every now and then, the currently active Key-Chain needs to be unlocked, the in-web-browser securely maintained private key-decrypted and thus the symmetric passwords needs to be retrieved from user (somehow).

Please enjoy the ride. The main rationale behind this article is for you to get a better understanding of the overall architecture.

When it comes to GridScript to user data user interface global data requests targeting user, GRIDNET OS supports two distinct data request flows:

  • Local Data Requests: Originate from browser-based UI dApps (e.g., Wallet requesting password for KeyChain access)
  • Remote Data Requests: Originate from the GRIDNET Core full node (e.g., requesting transaction confirmation)

Both flows share the same UI presentation layer but have different routing and error handling mechanisms.

Architecture Overview

┌─────────────────────────────────────────────────────────────────────┐
│                           GRIDNET OS Browser                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌──────────────┐     ┌──────────────────┐    ┌──────────────────┐ │
│  │              │     │                  │    │                  │ │
│  │  Wallet UI   │────▶│  KeyChainManager │    │   Other dApps    │ │
│  │    dApp      │     │                  │    │                  │ │
│  └──────────────┘     └────────┬─────────┘    └────────┬─────────┘ │
│                                 │                        │           │
│                      Local Requests                      │           │
│                                 │                        │           │
│  ┌──────────────────────────────▼────────────────────────▼─────────┐ │
│  │                          CVMContext                             │ │
│  │  ┌─────────────────┐    ┌──────────────┐   ┌─────────────────┐ │ │
│  │  │ mPendingUITasks │    │  mGUITasks   │   │ processVMMeta-  │ │ │
│  │  │  (Local Map)    │    │   (Queue)    │   │ DataRequest()   │ │ │
│  │  └─────────────────┘    └──────────────┘   └─────────────────┘ │ │
│  └────────────────────────────────┬───────────────────────────────┘ │
│                                   │                                  │
│  ┌────────────────────────────────▼───────────────────────────────┐ │
│  │                    UI Layer (index.html)                       │ │
│  │                 processUserActionRequest()                     │ │
│  │                     SweetAlert2 Prompts                        │ │
│  └────────────────────────────────────────────────────────────────┘ │
│                                   │                                  │
└───────────────────────────────────┼──────────────────────────────────┘
                                    │
                          WebSocket Connection
                                    │
                         ┌──────────▼──────────┐
                         │   GRIDNET Core      │
                         │    Full Node        │
                         └─────────────────────┘

Local Data Request Flow

Overview

Local data requests are initiated by UI dApps within the browser and are handled entirely client-side without network communication.

Sequence Diagram: Wallet Signing Transaction with KeyChain

┌─────────┐     ┌─────────────────┐     ┌────────────┐     ┌──────────┐     ┌────────┐
│ Wallet  │     │ KeyChainManager │     │ CVMContext │     │index.html│     │  User  │
│  dApp   │     │                 │     │            │     │    UI    │     │        │
└────┬────┘     └────────┬────────┘     └──────┬─────┘     └─────┬────┘     └───┬────┘
     │                   │                      │                  │              │
     │ signTransaction() │                      │                  │              │
     ├──────────────────▶│                      │                  │              │
     │                   │                      │                  │              │
     │                   │ getActiveKeyChain()  │                  │              │
     │                   ├─────────────────────▶│                  │              │
     │                   │                      │                  │              │
     │                   │ unlock() required?   │                  │              │
     │                   ├──────────┐           │                  │              │
     │                   │          │           │                  │              │
     │                   │◀─────────┘           │                  │              │
     │                   │                      │                  │              │
     │                   │ _promptForPassword() │                  │              │
     │                   ├──────────┐           │                  │              │
     │                   │          │           │                  │              │
     │                   │◀─────────┘           │                  │              │
     │                   │                      │                  │              │
     │                   │ registerLocalUI-     │                  │              │
     │                   │ ResponseCallback()   │                  │              │
     │                   ├─────────────────────▶│                  │              │
     │                   │                      │                  │              │
     │                   │                      │ Store in        │              │
     │                   │                      │ mPendingUITasks │              │
     │                   │                      ├──────────┐      │              │
     │                   │                      │          │      │              │
     │                   │                      │◀─────────┘      │              │
     │                   │                      │                  │              │
     │                   │ addGUITask()         │                  │              │
     │                   ├─────────────────────▶│                  │              │
     │                   │                      │                  │              │
     │                   │                      │ Mark as          │              │
     │                   │                      │ isLocalRequest  │              │
     │                   │                      ├──────────┐      │              │
     │                   │                      │          │      │              │
     │                   │                      │◀─────────┘      │              │
     │                   │                      │                  │              │
     │                   │                      │ notifyNewUser-  │              │
     │                   │                      │ ActionRequest() │              │
     │                   │                      ├─────────────────▶│              │
     │                   │                      │                  │              │
     │                   │                      │                  │ Show Swal    │
     │                   │                      │                  │ Password     │
     │                   │                      │                  │ Prompt       │
     │                   │                      │                  ├─────────────▶│
     │                   │                      │                  │              │
     │                   │                      │                  │              │ Enter
     │                   │                      │                  │◀─────────────┤ Password
     │                   │                      │                  │              │
     │                   │                      │ handleResponse() │              │
     │                   │                      │◀─────────────────┤              │
     │                   │                      │                  │              │
     │                   │                      │ registerUserData-│              │
     │                   │                      │ Response()       │              │
     │                   │                      ├──────────┐       │              │
     │                   │                      │          │       │              │
     │                   │                      │ Detect   │       │              │
     │                   │                      │ Local    │       │              │
     │                   │                      │◀─────────┘       │              │
     │                   │                      │                  │              │
     │                   │ handleLocalUI-       │                  │              │
     │                   │ Response()           │                  │              │
     │                   │◀─────────────────────┤                  │              │
     │                   │                      │                  │              │
     │                   │ Unlock with password │                  │              │
     │                   ├──────────┐           │                  │              │
     │                   │          │           │                  │              │
     │                   │◀─────────┘           │                  │              │
     │                   │                      │                  │              │
     │                   │ Sign with PrivKey    │                  │              │
     │                   ├──────────┐           │                  │              │
     │                   │          │           │                  │              │
     │                   │◀─────────┘           │                  │              │
     │                   │                      │                  │              │
     │ Signed TX         │                      │                  │              │
     │◀──────────────────┤                      │                  │              │
     │                   │                      │                  │              │

Detailed Flow Description

  1. Wallet Initiates Transaction
// In Wallet UI dApp
async function sendTransaction(amount, recipient) {
    try {
        const keyChain = await CVMContext.getInstance()
            .getKeyChainManager()
            .getActiveKeyChain(this); // 'this' is the process handle
        
        if (!keyChain) {
            throw new Error("No active keychain");
        }
        
        // Sign transaction with keychain
        const signature = await signWithKeyChain(keyChain, txData);
        // ... proceed with transaction
    } catch (error) {
        handleError(error);
    }
}
  • KeyChainManager Checks Lock Status
  • If unlocked for this process → proceed to signing
  • If locked → initiate password prompt
  • Password Prompt Flow
// In KeyChainManager._promptForPassword()
const promise = new Promise(async (resolve, reject) => {
    const reqID = this.mVMContext.genRequestID();
    
    // Register callback FIRST
    this.mVMContext.registerLocalUIResponseCallback(
        reqID, 
        processID,
        (answer) => { // Success callback
            cleanup();
            resolve(answer);
        },
        (error) => { // Error callback
            cleanup();
            reject(error);
        },
        PASSWORD_PROMPT_TIMEOUT_MS
    );
    
    // Then create GUI task
    const taskEnqueued = this.mVMContext.addGUITask({
        id: reqID,
        reqID: reqID,
        processID: processID,
        type: eUITTaskType.request,
        dataType: eDataRequestType.password,
        caption: "Unlock Keychain Access",
        data: [`Unlock requested by: ${requestingAppName}\n\nEnter your master password:`],
        defaultValue: "",
        secureInput: true
    });
});
  • UI Presentation
  • CVMContext marks task with isLocalRequest = true
  • Notification dispatched to index.html
  • SweetAlert2 shows password prompt
  • No network error on failure
  • Response Handling
// In index.html handleResponse()
if (!CVMContext.getInstance().registerUserDataResponse(response)) {
    // Only show error for remote requests
    if (!isLocalRequest) {
        Swal.fire({
            icon: 'error',
            title: 'No connection',
            text: 'I could not deliver your response!'
        });
    }
}

Remote Data Request Flow

Overview

Remote data requests originate from the GRIDNET Core full node and require network communication for both the request and response.

Sequence Diagram: Full Node Requesting Transaction Confirmation

┌─────────────┐     ┌────────────┐     ┌──────────┐     ┌────────┐     ┌────────┐
│  Full Node  │     │ CVMContext │     │index.html│     │ Wallet │     │  User  │
│             │     │            │     │    UI    │     │  dApp  │     │        │
└──────┬──────┘     └──────┬─────┘     └─────┬────┘     └───┬────┘     └───┬────┘
       │                   │                  │              │              │
       │ VMMetaData        │                  │              │              │
       │ (dataRequest)     │                  │              │              │
       ├──────────────────▶│                  │              │              │
       │                   │                  │              │              │
       │                   │ processVMMeta-   │              │              │
       │                   │ DataRequest()    │              │              │
       │                   ├──────────┐       │              │              │
       │                   │          │       │              │              │
       │                   │◀─────────┘       │              │              │
       │                   │                  │              │              │
       │                   │ Check KeyChain   │              │              │
       │                   │ (if QRIntent)    │              │              │
       │                   ├──────────────────┼─────────────▶│              │
       │                   │                  │              │              │
       │                   │ Not handled      │              │              │
       │                   │◀─────────────────┼──────────────┤              │
       │                   │                  │              │              │
       │                   │ addGUITask()     │              │              │
       │                   │ (isLocalRequest  │              │              │
       │                   │  = false)        │              │              │
       │                   ├──────────┐       │              │              │
       │                   │          │       │              │              │
       │                   │◀─────────┘       │              │              │
       │                   │                  │              │              │
       │                   │ notifyNewUser-   │              │              │
       │                   │ ActionRequest()  │              │              │
       │                   ├─────────────────▶│              │              │
       │                   │                  │              │              │
       │                   │                  │ Show Swal    │              │
       │                   │                  │ Confirm TX   │              │
       │                   │                  ├─────────────────────────────▶│
       │                   │                  │              │              │
       │                   │                  │              │              │ Confirm
       │                   │                  │◀─────────────────────────────┤
       │                   │                  │              │              │
       │                   │ handleResponse() │              │              │
       │                   │◀─────────────────┤              │              │
       │                   │                  │              │              │
       │                   │ registerUserData-│              │              │
       │                   │ Response()       │              │              │
       │                   ├──────────┐       │              │              │
       │                   │          │       │              │              │
       │                   │ No local │       │              │              │
       │                   │ task     │       │              │              │
       │                   │◀─────────┘       │              │              │
       │                   │                  │              │              │
       │                   │ Prepare VMeta    │              │              │
       │                   │ Response         │              │              │
       │                   ├──────────┐       │              │              │
       │                   │          │       │              │              │
       │                   │◀─────────┘       │              │              │
       │                   │                  │              │              │
       │ VMMetaData        │                  │              │              │
       │ (dataResponse)    │                  │              │              │
       │◀──────────────────┤                  │              │              │
       │                   │                  │              │              │

Detailed Flow Description

  1. Full Node Initiates Request
  • Sends VMMetaData message with dataRequest entry
  • Contains request type, caption, data fields
  1. CVMContext Processing
// In processVMMetaDataRequest()
case eDataRequestType.boolean:
    this.addGUITask({
        id: request.id,
        reqID: request.id,
        processID: request.processID,
        type: eUITTaskType.request,
        dataType: request.dataType,
        caption: this.mTools.arrayBufferToString(request.data[0]),
        data: request.data[1],
        isLocalRequest: false // Explicitly remote
    });
    break;
  • QRIntentAuth Special Handling
  • First attempts local KeyChain signing
  • Falls back to QR code display if not available
  • Response Transmission
// In registerUserDataResponse() for remote
let entry = new CVMMetaEntry(
    eVMMetaEntryType.dataResponse, 
    response.reqID, 
    response.dataFields
);
let section = new CVMMetaSection(eVMMetaSectionType.notifications);
section.addEntry(entry);
gMetaGenerator.addSection(section);

let msg = new CNetMsg(
    eNetEntType.VMMetaData, 
    eNetReqType.process, 
    gMetaGenerator.getPackedData()
);

return this.sendNetMsg(msg);

Implementation Examples

Example 1: Wallet dApp with Transaction Signing

// Wallet.js
class CWallet extends CWindow {
    async sendTransaction(recipient, amount) {
        try {
            // Build transaction
            const tx = this.buildTransaction(recipient, amount);
            
            // Get active keychain (may trigger password prompt)
            const keyChain = await this.mVMContext
                .getKeyChainManager()
                .getActiveKeyChain(this);
            
            if (!keyChain) {
                this.showError("No active keychain. Please activate one.");
                return;
            }
            
            // Sign transaction
            const signature = this.mVMContext
                .getCryptoFactory()
                .sign(keyChain.getPrivKey(), tx.hash);
            
            // Submit to network
            await this.submitTransaction(tx, signature);
            
        } catch (error) {
            if (error.message.includes("cancelled")) {
                this.showInfo("Transaction cancelled");
            } else {
                this.showError(`Transaction failed: ${error.message}`);
            }
        }
    }
}

Example 2: Handling Both Local and Remote Auth Requests

// Enhanced processVMMetaDataRequest for QRIntentAuth
case eDataRequestType.QRIntentAuth:
    // Log the request
    this.logAppEvent(
        `Auth request ${request.id} received for process ${request.processID}`,
        null, 
        eLogEntryType.info
    );
    
    // Only try KeyChainManager if we have a valid process handle
    if (targetProcessHandle) {
        try {
            // Attempt local keychain signing
            const handledLocally = await this.mKeyChainManager
                .signAuthenticationRequest(targetProcessHandle, request);
                
            if (handledLocally) {
                // Successfully handled by keychain
                this.logAppEvent(
                    `Auth request ${request.id} handled by keychain manager.`,
                    null,
                    eLogEntryType.info
                );
                return true;
            }
        } catch (keychainError) {
            // Log error but continue to QR fallback
            this.logAppEvent(
                `Keychain signing failed: ${keychainError.message}`,
                null,
                eLogEntryType.warning
            );
        }
    }
    
    // Fallback to QR code display
    let qr = CQRIntent.instantiate(request.defaultValue);
    if (qr) {
        this.addGUITask({
            id: request.id,
            reqID: request.id,
            processID: request.processID,
            type: eUITTaskType.request,
            dataType: request.dataType,
            caption: this.mTools.arrayBufferToString(request.data[0]),
            data: qr,
            isLocalRequest: false // Remote request
        });
    }
    break;

Edge Cases and Error Handling

1. Network Disconnection During Remote Request

// In handleResponse() - only show error for remote requests
if (!CVMContext.getInstance().registerUserDataResponse(response)) {
    if (!isLocalRequest) {
        // Remote request failed due to network
        Swal.fire({
            icon: 'error',
            title: 'No connection',
            text: 'I could not deliver your response!',
            footer: 'Wait for a re-connection..'
        });
    }
    // Local requests fail silently (handled by promise rejection)
}
  1. Timeout Handling
// Local request timeout (in KeyChainManager)
const timeoutTimer = setTimeout(() => {
    const task = this.mPendingUITasks.get(reqID);
    if (task) {
        this.mPendingUITasks.delete(reqID);
        if (task.reject) {
            task.reject(new Error("UI response timeout"));
        }
    }
}, PASSWORD_PROMPT_TIMEOUT_MS);
  1. Process ID Validation
// In handleLocalUIResponse()
const task = this.mPendingUITasks.get(reqID);
if (task && response.processID !== task.processID) {
    this.mTools.logEvent(
        `Process ID mismatch: expected ${task.processID}, got ${response.processID}`,
        eLogEntryCategory.localSystem,
        1,
        eLogEntryType.warning
    );
}
  1. User Cancellation
// In processUserActionRequest()
const handleCancellation = (reason = "User cancelled") => {
    if (isLocalRequest) {
        // Notify the waiting promise
        CVMContext.getInstance().cancelLocalUIRequest(reqID, reason);
    }
    // Remote cancellations don't need special handling
};

Security Considerations

  1. Process Isolation: Each dApp has its own unlock state in KeyChainManager
  2. Timeout Protection: All UI prompts have timeouts to prevent indefinite locks
  3. No Cross-Process Access: A dApp cannot access another dApp’s keychain session
  4. Password Hash Storage: Only temporary, cleared on timeout or lock
  5. Network Error Isolation: Local operations don’t expose network state

Troubleshooting Guide

Common Issues

  1. “No pending UI task found”
  • Cause: Response arrived after timeout
  • Solution: Increase timeout or optimize user flow
  1. “Process ID mismatch”
  • Cause: Response routed to wrong process
  • Solution: Ensure processID is preserved throughout flow
  1. “Failed to enqueue password prompt”
  • Cause: UI system not ready
  • Solution: Check VMContext initialization
  1. Connection errors for local operations
  • Cause: Missing isLocalRequest flag
  • Solution: Verify addGUITask properly sets flag

This documentation provides a comprehensive understanding of both local and remote data request flows in GRIDNET OS, enabling developers to properly implement secure UI interactions in their dApps.