GridScript: The Language of the Decentralized State Machine — A Comprehensive Guide

Preface — The Language That Became an Operating System

In 1970, Charles H. Moore created Forth — a programming language so minimal, so close to the metal, that it could run on hardware with 8 kilobytes of memory. Forth was not elegant in the way that Lisp was elegant, nor structured in the way Pascal was structured. It was something else entirely: a language where the programmer thought in terms of a stack, where every operation consumed values and produced values, where the boundary between the language and the machine dissolved into nothing. NASA used Forth to control spacecraft. Telescope operators used it to point instruments at distant galaxies. It was the language of people who needed absolute control over every byte.Half a century later, a different kind of machine needed a language with those same properties — not a spacecraft, but a decentralized state machine. A machine that does not exist in any single location, that is maintained by independent operators across the globe, that must execute every instruction identically on every node, and that must do so with mathematical certainty. This machine is GRIDNET OS — the world’s first decentralized operating system. And its language is GridScript.GridScript is derived from Forth, but it is not Forth. Where Forth was designed to control hardware, GridScript was designed to control consensus. Where Forth communicated with serial ports and memory-mapped I/O, GridScript communicates with a Merkle Patricia Trie holding the global state of a decentralized network. Where Forth programs ran on a single processor, GridScript programs are replicated and executed across every node in the network — deterministically, verifiably, and irreversibly. And where Forth execution is essentially free — instructions run as fast as the processor allows with no accounting — every single GridScript instruction consumes ERG (Execution Resource Gas), making it a metered computation model where unbounded execution is impossible by design. This single difference transforms GridScript from a language into an economic mechanism: computation has a price, and that price prevents denial-of-service attacks against the network.GridScript serves simultaneously as:

  • The transaction language — every blockchain transaction is a GridScript program compiled to bytecode
  • The smart contract language — decentralized applications are written, deployed, and invoked in GridScript
  • The system shell — operators manage GRIDNET Core nodes through an interactive GridScript terminal
  • The consensus language — all nodes execute the same GridScript deterministically to reach agreement on state
  • The query language — all data retrieval from the decentralized state machine is performed through GridScript execution

This article is a comprehensive guide to GridScript — its architecture, its execution model, its role as the backbone of a decentralized service-oriented architecture, and its practical use in building applications that run without servers, without central authorities, and without trust in any single entity.

I. Decentralized Processing Threads — The Cornerstone of Everything

If you understand only one concept in GridScript, let it be this: Decentralized Processing Threads (DPTs). They are the mechanism through which every interaction with GRIDNET OS occurs — every value transfer, every smart contract call, every data query, every file operation. DPTs are to GRIDNET OS what HTTP requests are to the web, except they execute Turing-complete code inside a metered virtual machine on a decentralized network.

BT and CT — Begin Thread, Commit Thread

The two most important commands in GridScript are BT (Begin Thread) and CT (Commit Thread). Together, they define the lifecycle of a transaction:

BT
cd /YourDomainAddress
send RecipientAddress 1000000000000000000
CT

When BT executes, it creates a new sub-thread — a sandbox where subsequent GridScript commands are accumulated but not executed. The commands are recorded into a code buffer. Inline commands like send are automatically converted to their explicit stack-based forms (sendEx) for serialization. The thread tracks its own ERG (Execution Resource Gas) consumption.When CT executes, it collects code from all ready threads, compiles the accumulated GridScript into bytecode, signs the transaction with the user’s private key, and submits it to the network. The transaction then propagates to all nodes, where it is re-executed in kernel mode — deterministically, identically, on every machine — and the resulting state changes are committed to the global Merkle Patricia Trie.

Ephemeral vs. Committed — The Two Modes of DPTs

Not all DPTs result in blockchain transactions. This distinction is fundamental:Committed DPTs follow the full BT → accumulate → CT cycle. They produce signed transactions that modify the global state. A value transfer, a smart contract deployment, an identity registration — these are committed DPTs.Ephemeral DPTs execute GridScript on a remote GRIDNET Core node without committing anything to the blockchain. They are read-only queries against the current state of the decentralized state machine. When the Blockchain Explorer displays a list of recent transactions, it is using ephemeral DPTs. When the Wallet dApp retrieves an account balance, it is using an ephemeral DPT. The GridScript executes, reads from the state trie, produces a BER-encoded result, and the thread is discarded. No transaction is signed. No bytecode is stored. No ERG is charged to the user’s account.This dual nature — the same mechanism serving both state-modifying transactions and read-only queries — is what makes DPTs the cornerstone of the entire GRIDNET OS architecture. Every UI dApp, every data retrieval operation, every interaction that a user has with the decentralized state machine flows through a DPT.

Multi-Thread Transactions

GridScript supports formulating up to five threads simultaneously (MAX_THREADS_COUNT = 5), enabling atomic multi-operation transactions:

BT
cd /DomainA
send Recipient1 1000000000000000000
BT
cd /DomainB
send Recipient2 2000000000000000000
CT      \ Commits all ready threads as one atomic transaction

Thread management commands provide fine-grained control: RT (Resume Thread), ST (Suspend Thread), PT (Print Thread — show accumulated code), FT (Focus Thread — switch between threads), and AT (Abort Thread — discard accumulated code).

II. GridScript as a Decentralized Service-Oriented Architecture

Stop and consider what is actually happening when a user opens the Wallet UI dApp or the Blockchain Explorer in their browser.Every balance displayed. Every transaction listed. Every block detail rendered. Every identity resolved. None of it comes from a traditional server. There is no PHP backend. There is no .NET API. There is no REST endpoint hosted in a data center. Every single piece of data the user sees on screen is retrieved through GridScript code executing on remote GRIDNET Core nodes — decentralized machines operated independently across the network.This is the GridScript Service-Oriented Architecture (SOA), and it is what makes the entire GRIDNET OS dApp ecosystem work.

The Data Flow Pipeline

The SOA pipeline operates in six stages:

  1. JavaScript formulates the request — Browser-side code in VMContext.js (which exports the CVMContext singleton) constructs a GridScript data request. This might be a balance query, a transaction search, a block listing, or a domain lookup.
  2. BER encoding — The request is serialized using BER (Basic Encoding Rules) — an efficient binary encoding format derived from ASN.1. BER is not JSON. It is not Protocol Buffers. It is a compact, self-describing binary format that can represent complex nested data structures with minimal overhead. Web Workers perform the encoding off the main thread to keep the UI responsive.
  3. Double-encrypted transport — The BER-encoded request is wrapped in ECC (Elliptic Curve Cryptography) encryption at the application level, then transmitted through a TLS-secured WebSocket. Two independent encryption layers. An attacker who compromised TLS would still face a second cryptographic barrier.
  4. GridScript VM execution — The remote GRIDNET Core node receives the request, decodes it, and executes the corresponding GridScript handler. The VM traverses the Merkle Patricia Trie — the global state database — reading account data, transaction histories, block headers, identity tokens, or whatever the request demands.
  5. BER-encoded response — The results are packaged into structured BER metadata — organized into typed sections (directoryListing, searchResults, notifications, transactionInfo, blockInfo, domainInfo) containing typed entries. This structured format enables rich data exchange between the VM and the browser.
  6. CVMContext receives and dispatches — The BER-encoded response arrives back through the same double-encrypted, onion-routed channel. The in-browser GRIDNET OS subsystem — the CVMContext singleton — receives the raw data, decodes it using CVMMetaParser, and dispatches it through two distinct mechanisms:
    • Active waiting (request-response) — A UI dApp that initiated a specific data request is actively awaiting the response. It holds a pending Promise or async callback keyed to the request ID it generated. When CVMContext encounters that request ID in the incoming BER data, it resolves the waiting call directly — the dApp receives exactly the data it asked for. This is the primary pattern for queries: the Wallet asks for a balance, the Explorer asks for a block list, and each awaits the specific response.
    • Passive notification (event-driven) — UI dApps may also register general-purpose event listeners for broad categories of data — new block announcements, VM state changes, commit status transitions, DFS updates, incoming network messages. These listeners fire whenever CVMContext receives matching data, regardless of whether the dApp initiated the request. A new block notification may reach both the Explorer and the Wallet simultaneously; a commit confirmation may notify multiple dApps that share a transaction context. The dApp does not ask for this data — it arrives because the dApp has expressed interest in a category of events.

    This dual dispatch model — active request-response correlation and passive event subscription — operating simultaneously through the same CVMContext singleton is what gives GRIDNET OS UI dApps both the responsiveness of traditional client-server applications and the real-time reactivity of event-driven systems.

  7. UI dApp renders — Whether actively awaiting a response or passively notified of an event, the UI dApp instance receives the decoded data, processes it according to its own logic, and updates its Shadow DOM. The user sees data on screen and has no idea that what just happened bears no resemblance to a conventional HTTP request-response cycle. From the user’s perspective, it is indistinguishable from a centralized web application — but beneath the surface, every byte traveled through a decentralized, double-encrypted, censorship-resistant pipeline with no single point of failure.
GridScript VM Decentralized SOA Architecture
The GridScript SOA pipeline — from browser-side JavaScript through double-encrypted transport to remote GridScript VM execution, with BER-encoded responses flowing back through CVMContext to individual UI dApp instances.

Performance That Defies Expectations

The measured balance update latency — the round-trip time from browser request to rendered result through this entire double-encrypted, BER-encoded pipeline — is 20–45 milliseconds on a local network (WAN performance varies with node proximity) on a local network. This is on par with, and in many cases faster than, conventional centralized web frameworks querying a local database.This performance is possible because BER encoding is remarkably efficient for the structured data GRIDNET OS works with. Unlike JSON, which carries field names as human-readable strings in every message, BER uses numeric tags and length prefixes, producing payloads that are significantly smaller. Unlike Protocol Buffers, BER requires no schema compilation step — the encoding is self-describing. The VM Meta-Data Protocol builds on BER with a Sections → Entries structure, where each section has a typed purpose and each entry carries a request ID for correlation — enabling multiple concurrent asynchronous requests to be in flight simultaneously.

How dApps Use This Architecture

The Blockchain Explorer UI dApp provides a vivid example. When a user navigates to the Blocks view, the Explorer’s JavaScript constructs a GridScript request to fetch recent blocks with sorting and filtering parameters. The request is BER-encoded, dispatched through the encrypted WebSocket, and routed to a GRIDNET Core node. On that node, the GridScript VM executes a stateful iterator — a cursor that traverses the block chain with pagination support, filter matching, and sort optimization. The VM enhanced this capability with stateful iterators that remember position between calls, eliminating the need to re-traverse from the beginning for each page of results. The results — block headers with height, timestamp, transaction count, miner ID, difficulty, reward — are BER-encoded into a searchResults section and returned. The browser decodes and renders a Tabulator data grid with cyberpunk aesthetics.The Wallet dApp follows the same pattern for balance queries, transaction history, identity resolution, and token pool management. The old Wallet even managed off-chain state channels through this SOA layer — listing outgress channels, selecting token pools, and initiating cash-out operations, all through GridScript executing on remote nodes.

The BER Meta-Data Protocol

Communication between browser and GRIDNET Core follows a structured protocol built on BER encoding:

// JavaScript side — constructing a request
const generator = new CVMMetaGenerator();
generator.addRAWGridScriptCmd(gridScriptCode, requestID, processID, vmID);
const berData = generator.finalize();
// Send through encrypted WebSocket...

Architecture reference (GRIDNET Core internal):

// On the GRIDNET Core side — C++ processing
CVMMetaGenerator gen;
gen.beginSection(eVMMetaSectionType::searchResults);
gen.addTransactionInfo(txDesc, reqID, appID, vmID);
gen.endSection();
gen.finalize();
vector<uint8_t> data = gen.getData();

The protocol supports dozens of entry types: GridScriptCode, terminalData, transactionInfo, blockInfo, domainInfo, searchResults, fileContent, stateLessChannelElement, and many more. Each entry carries metadata for request-response correlation, enabling the browser to match responses to the specific UI component that initiated the request.The three-tier data hierarchy — Level 1 (raw transaction/receipt on-chain data), Level 2 (core info containers), Level 3 (BER-compatible description objects for transactions, blocks, and domains) — ensures that the same data structures are serialized identically in C++ and JavaScript, achieving full parity between the native Core implementation and the browser-side ECMA6 objects.

III. Where GridScript Runs — The Six Execution Contexts

GridScript is not confined to a single execution environment. It runs in six distinct contexts, each with different capabilities and security restrictions:

1. SSH — Direct Connection to GRIDNET Core

The most direct way to interact with GridScript. Connect via SSH to a running GRIDNET Core node and you have a full interactive terminal — the Forth heritage made tangible. Type commands, watch the stack, formulate transactions, manage the node. Every command in the GridScript vocabulary is available, subject only to authentication level.

\ SSH session example
10 20 + .          \ prints 30
." Hello, GRIDNET!" cr
balance 'MyDomain'
.                  \ prints balance in attoGNC

2. Local Instance — Ctrl+E in GRIDNET Core

When running GRIDNET Core directly, pressing Ctrl+E switches from the Events View to the Terminal View — an embedded GridScript console. This provides the same capabilities as SSH but with physical access to the machine. Certain administrative commands (like shutdown or firewall) may require this local context.

3. Terminal UI dApp — Browser-Based Terminal

The Terminal dApp brings GridScript to the browser. Built on xterm.js, it provides a full terminal experience within the GRIDNET OS desktop environment. The Terminal registers for VM metadata callbacks, DFS message events, and GridScript result notifications through CVMContext — the same SOA pipeline used by all dApps. Commands entered in the browser terminal are transmitted to a GRIDNET Core node for execution, with results streamed back in real-time.

4. Smart Contract Execution — On-Chain Bytecode

When a transaction is committed, its GridScript is compiled to bytecode and executed in kernel mode across all network nodes. Kernel mode is the consensus context — fully deterministic, no terminal I/O, no file system access, no user interaction. The VM checks REG_KERNEL_THREAD to enforce these restrictions. Every node must produce identical results for the same input, ensuring consensus.Smart contracts deployed via BC/EC (Begin Code / End Code) are stored as bytecode in the state trie:

BT
cd /YourDomain/contracts
BC
cd /YourDomain
getVar 'counter'
1 +
0 "counter" setVarEx
EC
CT

The bytecode format uses sequential opcode assignment starting from BASE_OPCODE_ID = 9. Single-byte encoding for opcodes ≤ 127, two-byte encoding for higher values. The order of codewords is immutable — new commands are always appended at the end. Reordering would break every compiled contract on the blockchain.

5. Implicit via JavaScript APIs — VMContext.js

This is the SOA context described in the previous section. JavaScript code in the browser generates GridScript automatically — the developer may not even realize GridScript is being executed. When a dApp calls a balance query or transaction search through CVMContext, the JavaScript constructs GridScript data requests, BER-encodes them, and dispatches them to remote nodes. The GridScript execution is implicit, invisible to the end user.

6. User Interactions with UI dApps

Drag a file in the File Manager. Send GNC in the Wallet. Deploy an identity token. Every user action in a UI dApp is ultimately formulated as GridScript — making it reproducible across the entire decentralized system once committed. The Wallet dApp pioneered this architecture: JavaScript ECMA6 formulates GridScript instruction sequences, Web Workers perform BER encoding and GridScript compilation and ECC signing off the main thread, and compiled bytecode is dispatched through onion-routed encrypted WebSocket channels.

The Security Model — Permission Layers in Depth

Every one of GridScript’s 290+ codewords carries 18 security properties that are checked at runtime before the instruction executes. This is not a coarse-grained permission system — it is per-instruction, per-context security enforcement. Understanding these layers is essential for any developer building on GRIDNET OS.

Permission Levels

From least restrictive to most restrictive:

Level 1: Public Commands — Arithmetic (+, -, *, /), stack operations (dup, drop, swap), comparison, and logic. These execute in any context — kernel mode, terminal, GUI, smart contract. No restrictions. A simple example:

10 20 + .    \ Works everywhere — kernel, terminal, GUI, smart contract
             \ Stack: 10 → 10 20 → 30 → (prints 30)

Level 2: Terminal-Only Commands (onlyFromTerminal = true) — BT, CT, keygen, BC/EC, setKey, node configuration. These cannot be called from kernel mode during consensus execution, nor remotely by untrusted code. They exist for interactive session management. If a smart contract attempted to call BT, the VM would reject it — you cannot formulate a transaction from within a transaction.

\ Terminal session — works:
BT
cd /MyDomain
send RecipientAddr 1000000000000000000
CT

\ Inside kernel mode (smart contract) — REJECTED:
\ BT → RUNTIME_ERROR: "command only available from terminal"

Level 3: Local Admin Commands (requiresLocalAdminCredentials = true) — shutdown, firewall. These require physical or local access to the machine and administrator authentication. An SSH session from a remote location cannot execute these without first authenticating with local admin credentials via sudo.

Level 4: Overwatch Commands (allowedOnlyByAnOverwatch = true) — Critical system modifications that only the network’s highest-privilege entities (overwatches) can perform. The VM checks REG_EXECUTING_BY_OVERWATCH (register 16) before allowing execution. This is the ultimate security gate.

Level 5: Kernel-Restricted Commands (allowedInKernelMode = false) — File I/O, erg management, network operations, blockchain queries like getChain, stepBack. These exist for terminal use but are forbidden during consensus execution because they would break determinism. If time (which reads the local clock) were allowed in kernel mode, different nodes would get different results, causing consensus divergence — a network split.

The 18 Security Properties Per Codeword

Each codeword is declared with these security properties, checked at runtime:

  • allowedInKernelMode — Can this execute during consensus? (true for state operations, false for I/O and config)
  • requiresLocalAdminCredentials — Needs local admin login?
  • onlyFromTerminal — Restricted to interactive terminal sessions?
  • requiresSecurityToken — Must a cryptographic security token be on the stack?
  • allowedOnlyByAnOverwatch — Reserved for super-admin entities?
  • allowToBeExecutedFromRAWCodeMode — Allowed inside BC/EC blocks?
  • baseERGCost — Minimum ERG units consumed per execution
  • inlineParamsCount — Expected number of inline parameters
  • hasBase58BinaryInLineParams / hasBase64BinaryInLineParams — Parameter encoding
  • reqStackWordsProceeding — Minimum required stack depth
  • isDataReadFunction — Reads from state storage (for query optimization)
  • doNotIncludeInSource — Exclude from compiled transaction source
  • Plus additional flags for hash codes, length tracking, and extended versions

This means the VM performs up to 18 checks before every single instruction executes. The overhead is minimal — boolean flag checks — but the security guarantee is absolute: no instruction can escape its designated context.

Security Registers — Identity and Privilege

GridScript maintains 48 special-purpose registers. Several are critical for security:

REG_CALLERS_ID (register 11) vs REG_AUTHENTICATED_SD (register 12) — This distinction is the critical security concept in GridScript. REG_CALLERS_ID identifies who signed and paid for the transaction — derived from the cryptographic signature. REG_AUTHENTICATED_SD identifies who has permission to execute the current operation. These can differ during delegation, proxy execution, or sponsored transactions:

\ Inside a smart contract — check both:
11 getReg     \ Read REG_CALLERS_ID — who SIGNED the transaction
              \ Stack: callers_identity_bytes
12 getReg     \ Read REG_AUTHENTICATED_SD — who HAS PERMISSION
              \ Stack: callers_identity_bytes authenticated_sd_bytes
fcomp         \ Compare them
              \ Stack: flag (are they the same entity?)
\ If different: delegation or proxy execution is occurring

⚠️ REG_AUTHENTICATED_SD must be cleared after a dApp exits — failure to do so causes privilege leakage where the next execution inherits the previous entity’s permissions.

REG_EXECUTING_BY_OVERWATCH (register 16) — When set, grants the highest privilege level. Only the system itself can set this during overwatch entity execution.

REG_KERNEL_THREAD (register 34) — The kernel mode flag. When set, execution is occurring as part of consensus. Only kernel threads can modify decentralized state. This register must never be user-modifiable.

REG_IS_THREAD_UI_AWARE (register 31) — Indicates whether the thread can perform UI operations. This register cannot be changed from user mode — preventing UI spoofing attacks where malicious code pretends to be a UI thread.

Security Tokens for Cross-Domain Access

Commands with requiresSecurityToken = true demand a cryptographic proof of authorization on the stack. The token layout (bottom to top):

\ Security token stack layout:
Signature     \ [0] Bottom — ECDSA/Schnorr signature over the token payload
Nonce         \ [1] Fresh nonce — prevents replay attacks
PublicKey     \ [2] Public key of the authorizing entity
DomainID      \ [3] Target state domain being accessed
FunctionID    \ [4] Top — specific function being invoked

The VM validates the security token by checking: (1) signature matches public key, (2) nonce is fresh, (3) permission is granted for the domain + function combination. If valid, REG_AUTHENTICATED_SD is updated to the authorized entity.

Determinism in Kernel Mode — What’s Allowed vs Forbidden

In kernel mode, every operation must be deterministic. The same inputs must produce the same outputs on every node in the network. Violations cause consensus divergence — a network split where nodes disagree on the state of the world.

GridScript VM Self-Protection Module
The GridScript VM’s self-protection module enforces determinism and security boundaries at the instruction level.
✅ Allowed in Kernel Mode ❌ Forbidden in Kernel Mode
Pure arithmetic (+, -, *, /) Random number generation
State trie reads/writes (getVar, setVarEx) Local timestamps (time — use block time instead)
Cryptographic operations with same keys File system access
Stack manipulation Network calls
BigInt operations User input (accept)
Value transfers (sendEx) ERG management (erg)
Smart contract calls (callEx) Key generation (keygen)

This is a key philosophical point: user activity is described in GridScript so it is reproducible. File drag operations, value transfers, identity registrations — everything. When stored on the blockchain, GridScript is compiled to bytecode. The language bridges the gap between human intent and deterministic machine execution.

IV. Stack-Based Fundamentals — The Forth Heritage

If you have never used a stack-based language, the mental model is simple:

  1. Everything is a word — commands, numbers, operators are all “words”
  2. Numbers push themselves onto the stack
  3. Words consume and produce stack values
  4. No parentheses needed — execution order is left-to-right, stack-driven
10 20 +     \ Push 10, push 20, add → stack contains 30
5 *         \ Push 5, multiply → stack contains 150
.           \ Print top of stack → outputs "150"

Traditional programming: result = (10 + 20) * 5GridScript (postfix): 10 20 + 5 * .

The Three Stacks

GridScript maintains three stacks:Data Stack (dStack) — The primary operand stack. All values pass through here. Maximum depth: 256. This is where you push numbers, pointers, addresses, and where operations consume and produce results.Return Stack (rStack) — Used for temporary storage and loop control. The >r command moves a value from the data stack to the return stack; r> moves it back. Loop constructs (DO...LOOP) use the return stack to track loop indices.

10 >r       \ Move 10 to return stack
20 30 +     \ Data stack: 50
r>          \ Move 10 back — data stack: 50 10

Meta Stack — Parallel to the data stack, tracking type information (integer, pointer, string, BigInt) for runtime type checking and debugging. Each value on the data stack has a corresponding type descriptor on the meta stack.

Stack Manipulation

The fundamental stack operations — inherited directly from Forth:

dup         \ ( a -- a a )        Duplicate top
swap        \ ( a b -- b a )      Swap top two
over        \ ( a b -- a b a )    Copy second to top
rot         \ ( a b c -- b c a )  Rotate top three
drop        \ ( a -- )            Remove top
pick        \ ( ... u -- ... xu ) Copy item at depth u
roll        \ ( ... u -- ... )    Move item at depth u to top
depth       \ ( -- n )            Current stack depth

Key Differences from Standard Forth

GridScript extends Forth significantly for its blockchain context:

  • Integer size: 64-bit integers plus arbitrary-precision BigInt for cryptocurrency values (1 GNC = 1018 attoGNC)
  • Security: Multi-layer permission system — kernel mode, terminal mode, admin credentials, security tokens, overwatch
  • Persistent state: Values stored in a Merkle Patricia Trie, not volatile memory
  • ERG metering: Every operation costs ERG (Execution Resource Gas), preventing infinite loops and resource exhaustion
  • Built-in cryptography: Key generation, signing, verification, encryption, hashing — all native commands
  • Thread formulation: BT/CT transaction threading has no Forth equivalent

Control Flow — Conditionals, Loops, and Word Definitions

GridScript inherits Forth’s complete control flow vocabulary. All branching constructs compile to (zbranch) and (branch) opcodes at compile time. These are fully available in kernel mode (on-chain smart contracts).

Conditionals — IF…THEN…ELSE

\ IF...THEN (basic conditional)
: is-positive ( n -- )
    0 > IF
        ." Positive!" cr
    THEN
;
5 is-positive        \ prints: Positive!

\ IF...ELSE...THEN
: abs ( n -- |n| )
    dup 0 < IF
        -1 *
    ELSE
        \ already positive, do nothing
    THEN
;
-7 abs .             \ prints: 7

\ Nested IF
: classify ( n -- )
    dup 0 = IF
        ." Zero" drop
    ELSE
        0 > IF ." Positive" ELSE ." Negative" THEN
    THEN
    cr
;

How it works: IF consumes a flag from the stack. If the flag is true (nonzero), execution continues to the next word. If false (0), execution jumps to ELSE (if present) or THEN. THEN marks the end of the conditional block — it is not the “then do this” of English; it means “resume here.”

Counted Loops — DO…LOOP and DO…+LOOP

\ DO...LOOP — iterates from start to limit-1
: count-to ( n -- )
    0 DO
        i .          \ 'i' pushes current loop index
    LOOP cr
;
5 count-to           \ prints: 0 1 2 3 4

\ DO...+LOOP — custom step size
: evens ( n -- )
    0 DO
        i .
    2 +LOOP cr
;
10 evens             \ prints: 0 2 4 6 8

\ Nested loops: 'j' gives the outer loop index
: grid ( rows cols -- )
    0 DO
        dup 0 DO
            i . ." ," j . ."  "
        LOOP cr
    LOOP drop
;

DO takes ( limit start -- ) from the stack. i pushes the current (inner) loop index; j pushes the outer loop index in nested loops. Both use the return stack internally.

Indefinite Loops — BEGIN…UNTIL and BEGIN…WHILE…REPEAT

\ BEGIN...UNTIL (post-test — always executes at least once)
: countdown ( n -- )
    BEGIN
        dup . 1 -
        dup 0 =
    UNTIL drop cr
;

\ BEGIN...WHILE...REPEAT (pre-test — may execute zero times)
: countdown2 ( n -- )
    BEGIN
        dup 0 >
    WHILE
        dup . 1 -
    REPEAT drop cr
;

UNTIL consumes a flag — if true, the loop exits; if false, it branches back to BEGIN. WHILE consumes a flag — if true, execution continues to REPEAT which branches back to BEGIN; if false, execution jumps past REPEAT.

Defining New Words

\ Colon definitions — the primary abstraction mechanism
: square ( n -- n² ) dup * ;
: cube ( n -- n³ ) dup square * ;
5 cube .              \ prints: 125

\ Constants
: SPEED 100 ;

\ Anonymous definitions (execution tokens)
:noname ( n -- n² ) dup * ;   \ pushes XT onto stack
5 swap execute .              \ prints: 25

\ CREATE...DOES> for defining words that create data
: constant ( n -- )
    CREATE ,
    DOES> @
;
100 constant MAX-SPEED
MAX-SPEED .                   \ prints: 100

Word definitions with : name ... ; are the fundamental way to build abstractions in GridScript. Every colon definition compiles to bytecode and can be called from other definitions, smart contracts, or interactive sessions.

GridScript++ — JavaScript-Like Branching

If Forth-style branching feels unfamiliar, GridScript++ provides JavaScript-compatible syntax including if/else, for, while, function, and more. GridScript++ is a separate language that can be invoked from GridScript via the evalGPP codeword. It compiles down to GridScript operations. For details, see:

Raw Memory Allocation — malloc/free in a Blockchain VM

GridScript provides something unusual for a blockchain virtual machine: raw, manual memory allocation akin to C’s malloc and free. While most blockchain VMs hide memory management behind abstractions, GridScript gives the developer direct control — and direct responsibility.

The Core Allocation Commands

alloc ( size -- addr ior ) — Allocates size bytes of memory, returning the address and an I/O result code (0 = success, nonzero = failure). This is GridScript’s malloc. Each byte allocated costs 1 ERG (ERG_COST_PER_RAM_BYTE = 1), so allocating 1024 bytes costs 1024 ERG in addition to the base instruction cost.

free ( addr -- ) — Frees a previously allocated memory block. The VM tracks every allocation internally — when you call free, the allocation is removed from the tracker. Memory leaks are detectable.

allot ( n -- ) — Allocates n bytes in the data space (dictionary area). Unlike alloc, this is a Forth-traditional word that advances the here pointer. The memory is not individually freeable — it persists until the VM session ends. Use allot for compile-time data structures; use alloc/free for runtime dynamic allocation.

here ( -- addr ) — Pushes the current data-space pointer. This is where the next allot or , (comma) will place data.

Memory Access Commands

  • ! ( value addr -- ) — Store a cell (64-bit value) at address
  • @ ( addr -- value ) — Fetch a cell from address
  • c! ( char addr -- ) — Store a single byte at address
  • c@ ( addr -- char ) — Fetch a single byte from address
  • cells ( n -- n*cellsize ) — Convert a count to cell-aligned byte size (multiply by 8 on 64-bit)
  • cmove ( src dest count -- ) — Copy count bytes from src to dest (forward copy)
  • cmove> ( src dest count -- ) — Copy bytes backward (safe for overlapping regions)
  • fill ( addr count char -- ) — Fill count bytes starting at addr with byte value char
  • memclr ( addr -- ) — Zero out a previously allocated memory region
  • mem ( -- ) — Display memory usage statistics (allocation tracker summary)

Worked Example: Allocate, Use, Free

\ Allocate 1024 bytes of memory
1024 alloc          \ Stack: addr 0
                    \ addr = pointer to allocated block
                    \ 0 = ior (success)
                    \ ERG cost: 1024 (bytes) + 1 (base) = 1025 ERG

drop                \ Drop ior, keep address
                    \ Stack: addr

\ Store the value 42 at the allocated address
dup 42 swap !       \ Stack: addr
                    \ dup → addr addr
                    \ 42 → addr addr 42
                    \ swap → addr 42 addr
                    \ ! → stores 42 at addr → Stack: addr

\ Fetch it back to verify
dup @               \ Stack: addr 42
                    \ dup → addr addr
                    \ @ → fetches cell at addr → addr 42

. cr                \ Print: 42 (newline)
                    \ Stack: addr

\ Store bytes at offsets
dup 65 over c!      \ Store 'A' (65) at addr+0
dup 1 + 66 over c!  \ Store 'B' (66) at addr+1
dup 2 + 67 over c!  \ Store 'C' (67) at addr+2

\ Read them back
dup c@ emit         \ Print: A
dup 1 + c@ emit     \ Print: B
dup 2 + c@ emit     \ Print: C
cr                  \ Output so far: "42\nABC\n"

\ Zero out the first 256 bytes
dup 256 0 fill      \ Fill 256 bytes with 0x00

\ Free the memory
free                \ Stack: (empty)
                    \ Allocation removed from tracker

\ Show memory statistics
mem                 \ Displays allocation tracker summary

How the VM Tracks Allocations

Internally, the GridScript VM maintains a memory region registry. Every allocation (whether from alloc, string creation, or data commands) is registered as a memory_region with a start address and end address. The VM uses this registry for two purposes:

  1. Bounds checking — Before every memory access (!, @, c!, c@, cmove, fill), the VM validates the target address against its registered memory allocations to verify the target address falls within a registered allocation. Out-of-bounds access throws a runtime error. This prevents buffer overflows and use-after-free vulnerabilities.
  2. Leak detection — When free is called, the allocation is unregistered. When the VM session ends, any remaining allocations are visible via mem. The VM can detect memory that was allocated but never freed.

All memory access is bounds-checked by the VM. Out-of-bounds access or use-after-free throws a runtime error — the VM validates every pointer against its allocation registry before any read or write.

This means GridScript achieves something remarkable: it gives you C-level memory control inside a blockchain VM, while maintaining safety guarantees that C itself does not provide. Every pointer is validated. Every access is bounds-checked. The cost is ERG — 1 ERG per byte allocated — creating an economic disincentive against wasteful memory use.

Debugging GridScript

GridScript provides several introspection tools that make debugging interactive and immediate. Unlike traditional debuggers that require breakpoints and stepping, GridScript’s stack-based nature means you can inspect the entire execution state at any point by inserting diagnostic words.

Stack Inspection

\ .s — Non-destructive stack dump with rich type info (the most important debug tool)
10 20 30 .s
\ Output:
\ [ Data-Stack Contents ] Depth: 3
\ # | Value | Source | Description | DataType
\ 0 | 30    | +      | result      | signedInteger
\ 1 | 20    | (lit)  | literal     | signedInteger
\ 2 | 10    | (lit)  | literal     | signedInteger

\ Debug a calculation step by step:
10 20 .s             \ Stack[2]: 10 20
+ .s                 \ Stack[1]: 30
5 * .s               \ Stack[1]: 150

\ depth — How many items are on the stack?
10 20 30 depth .     \ prints: 3

\ .r — Return stack contents (useful when debugging loops)
10 >r 20 >r
.r                   \ Shows return stack contents
r> r> drop drop

\ .m — Memory dump (shows raw buffer contents for pointers)
42 "hello" .m        \ Shows memory regions for stack values

\ .sr — All 48 special registers
.sr                  \ Dumps register contents (identity, permissions, ERG state, etc.)

ERG and Resource Monitoring

\ ergusage — How much ERG consumed so far (terminal-only)
ergusage             \ Shows current ERG consumption

\ mem — Memory allocation statistics
mem                  \ Shows all tracked allocations, sizes, and leak candidates

\ erg -getsandbox — Full sandbox execution stats
erg -getsandbox      \ Shows ERG consumed, ERG limit, ERG remaining, TX overhead

The compiler Codeword as a Debugging Tool

The compiler codeword (see Section V-A) is invaluable for debugging because it lets you compile GridScript snippets and inspect the resulting bytecode without committing anything:

\ Test-compile a snippet to verify syntax:
compiler "dup dup + ."
\ Shows: ✓ Compilation Successful, bytecode size, V2 hash

\ Test-compile a transaction's code before committing:
compiler "cd /MyDomain send Recipient 1000000000000000000"
\ Verify bytecode is correct before using BT/CT

Common Errors and How to Diagnose

Error Cause Diagnosis
Stack underflow Operation consumed more values than available on stack Insert .s before the failing operation to see what’s actually on the stack. Check stack effect comments ( before -- after ).
ERG exhausted / “out of ERG by N” Transaction exceeded its ERG Limit Use ergusage at intervals to find the expensive operation. Increase limit with erg -setlimit. Check loops — a 10,000-iteration loop with a 3-ERG body costs 30,000 ERG.
Invalid memory access Pointer to freed memory or out-of-bounds access Use mem to inspect allocations. Ensure all alloc/free pairs match. Check that pointers from string operations are still valid.
“command only available from terminal” Used a terminal-only command ([T]) inside a smart contract or kernel mode Commands like BT, CT, keygen, erg, time cannot run in kernel mode. Use their kernel-safe equivalents or restructure your code.
Compilation failed Invalid GridScript syntax Use compiler "your code here" to test compilation with detailed error output and hints.
Type mismatch Operation expected a different type on stack Use .m to inspect the metadata stack and see the types of each stack item.

Debugging Strategy: The .s Sandwich

When a word misbehaves, wrap its calls with .s:

\ Before:
my-broken-word

\ Debug version:
." Before: " .s cr
my-broken-word
." After: " .s cr

This immediately reveals what the word consumed and produced, making stack imbalances visible.

V. Command Reference — The GridScript Vocabulary

GridScript’s vocabulary encompasses approximately 290 commands (codewords), each assigned a sequential opcode starting from BASE_OPCODE_ID = 9. Commands are categorized by function. Each entry below shows the command name, its stack effect notation ( before -- after ), and a brief description. Commands marked [T] are terminal-only (not available in kernel/consensus mode).

Data Input

  • data64 ( -- ptr ) — Push base64Check-decoded binary data onto the stack
  • adata64 ( -- ptr ) — Advanced base64 data with metadata
  • data ( -- ptr ) — Push UTF-8 string data onto the stack
  • data58 ( -- ptr ) — Push base58Check-decoded binary data
  • adata ( -- ptr ) — Advanced data with metadata

Assertions

  • assert ( flag -- ) — Abort execution if flag is false (0)
  • assert0 ( flag -- ) — Abort execution if flag is true (nonzero)
  • anz ( n -- flag ) — Assert non-zero; abort if zero
  • az ( n -- flag ) — Assert zero; abort if non-zero

Memory Access

  • ! ( value addr -- ) — Store cell value at address
  • @ ( addr -- value ) — Fetch cell value from address
  • c! ( char addr -- ) — Store byte at address
  • c@ ( addr -- char ) — Fetch byte from address
  • cells ( n -- n*cellsize ) — Convert count to cell-aligned byte size
  • cmove ( src dest count -- ) — Copy bytes forward
  • cmove> ( src dest count -- ) — Copy bytes backward (safe for overlap)
  • fill ( addr count char -- ) — Fill memory region with byte value
  • align ( -- ) — Align data pointer to cell boundary
  • aligned ( addr -- aligned-addr ) — Return nearest cell-aligned address
  • allot ( n -- ) — Allocate n bytes in data space
  • here ( -- addr ) — Push current data-space pointer
  • alloc ( size -- addr ior ) — Allocate memory block (malloc-style)
  • free ( addr -- ) — Free previously allocated memory
  • memclr ( addr -- ) — Clear/zero a memory region
  • mem ( -- ) — Display memory usage statistics

Stack Manipulation

  • dup ( a -- a a ) — Duplicate top of stack (built-in primitive)
  • swap ( a b -- b a ) — Swap top two stack items (built-in primitive)
  • over ( a b -- a b a ) — Copy second item to top (built-in primitive)
  • rot ( a b c -- b c a ) — Rotate top three items (built-in primitive)
  • drop ( a -- ) — Remove top item from stack
  • pick ( ... u -- ... xu ) — Copy item at depth u to top
  • roll ( ... u -- ... ) — Move item at depth u to top
  • depth ( -- n ) — Push current stack depth
  • >r ( a -- ) R:( -- a ) — Move top of data stack to return stack
  • r> ( -- a ) R:( a -- ) — Move top of return stack to data stack
  • r@ ( -- a ) R:( a -- a ) — Copy top of return stack without removing

Arithmetic

  • + ( a b -- a+b ) — Addition
  • - ( a b -- a-b ) — Subtraction
  • * ( a b -- a*b ) — Multiplication
  • / ( a b -- a/b ) — Integer division
  • /mod ( a b -- rem quot ) — Division with remainder
  • mod ( a b -- rem ) — Modulo (remainder only)

Comparison and Logic

  • < ( a b -- flag ) — Signed less-than comparison
  • = ( a b -- flag ) — Equality comparison
  • u< ( a b -- flag ) — Unsigned less-than comparison
  • > ( a b -- flag ) — Greater than (signed). Equivalent to swap < in standard Forth.
  • comp ( addr1 addr2 len -- flag ) — Compare memory blocks byte-by-byte
  • fcomp ( ptr1 ptr2 -- flag ) — Full-pointer compare
  • and ( a b -- a&b ) — Bitwise AND
  • or ( a b -- a|b ) — Bitwise OR
  • xor ( a b -- a^b ) — Bitwise XOR
  • lshift ( a n -- result ) — Left bit-shift
  • rshift ( a n -- result ) — Right bit-shift

Type Conversion

  • >num ( u -- n ) — Interpret unsigned value as signed
  • >unum ( n -- u ) — Interpret signed value as unsigned
  • concat ( ptr1 ptr2 -- ptr3 ) — Concatenate two byte sequences

Output and Display

  • . ( n -- ) — Print signed number
  • u. ( u -- ) — Print unsigned number
  • cr ( -- ) — Output newline
  • emit ( char -- ) — Output single character
  • type ( addr len -- ) — Print string from address and length
  • .s ( -- ) — Display data stack contents (non-destructive)
  • .r ( -- ) — Display return stack
  • .rs ( -- ) — Display return stack (alternate format)
  • .m ( -- ) — Dumps memory regions pointed to by stack values. Shows raw buffer contents for pointers, ‘atomic stack value’ for non-pointers. Use .s for a comprehensive type-aware stack display.
  • .sr ( -- ) — Display special registers
  • .m58 ( ptr -- ) — Print pointer value as base58
  • hx ( -- ) — Set hexadecimal display mode
  • echo ( -- ) — Echo inline text to output
  • echoEx ( ptr -- ) — Echo text from stack pointer
  • count ( addr -- addr+1 len ) — Convert counted string to address+length
  • bl ( -- 32 ) — Push space character (ASCII 32)
  • base ( -- addr ) — Push address of number-base variable

Control Flow and Looping

  • (branch) ( -- ) — Unconditional branch (runtime)
  • (zbranch) ( flag -- ) — Branch if zero/false (runtime)
  • (do) ( limit start -- ) — DO loop setup (runtime)
  • (doParams) ( -- ) — DO parameter initialization
  • (loop) ( -- ) — LOOP increment and test (runtime)
  • (+loop) ( n -- ) — +LOOP increment by n and test (runtime)
  • (i) ( -- index ) — Push inner loop counter
  • (j) ( -- index ) — Push outer loop counter
  • exit ( -- ) — Exit current word definition

Compiler and Interpreter Internals

  • (;) ( -- ) — End of colon definition (runtime semantics)
  • (does) ( -- ) — DOES> runtime behavior
  • (lit) ( -- n ) — Push literal from instruction stream
  • (slit) ( -- addr len ) — Push string literal from instruction stream
  • (dlit) ( -- d ) — Push double-precision literal
  • (ulit) ( -- u ) — Push unsigned literal
  • : ( -- ) — Begin colon definition of a new word
  • :noname ( -- xt ) — Begin anonymous (unnamed) definition
  • create ( -- ) — Create a new dictionary entry
  • evaluate ( addr len -- ) — Evaluate string as GridScript source
  • execute ( xt -- ) — Execute an execution token
  • find ( addr -- xt flag ) — Find word in dictionary
  • ffindEx ( -- ) — Extended dictionary find
  • hidden ( -- ) — Toggle hidden flag on latest definition
  • interpret ( -- ) — Enter interpreter loop
  • latest ( -- xt ) — Push execution token of most recent definition
  • parse ( char -- addr len ) — Parse input stream up to delimiter character
  • prompt ( -- ) — Display interpreter prompt
  • quit ( -- ) — Reset stacks, return to interpreter
  • refill ( -- flag ) — Refill input buffer from source
  • source ( -- addr len ) — Push current input source address and length
  • state ( -- addr ) — Push address of compilation state variable
  • word ( char -- addr ) — Parse next word delimited by char
  • words ( -- ) — List all defined words in dictionary
  • xt>name ( xt -- addr len ) — Convert execution token to word name
  • see ( -- ) — Decompile the next word in input
  • >body ( xt -- addr ) — Get data-field address from execution token
  • >in ( -- addr ) — Push address of input-offset variable
  • accept ( c-addr +n1 -- +n2 ) — Accept user input into buffer
  • abort ( -- ) — Abort execution, clear stacks
  • abort-message ( flag c-addr u -- ) — Conditional abort with error message

Cryptographic Operations

  • keygen ( -- privPtr pubPtr ) — Generate ECDSA key pair [T]
  • keygenS ( -- privPtr pubPtr ) — Generate Schnorr key pair [T]
  • sign ( dataPtr -- sigPtr ) — Sign data with active private key
  • versig ( sigPtr dataPtr pubPtr -- flag ) — Verify ECDSA/Schnorr signature
  • checkSig ( sigPtr dataPtr pubPtr -- flag ) — Check signature validity
  • sig ( -- sigPtr ) — Get current transaction signature
  • enc ( dataPtr keyPtr -- cipherPtr ) — Encrypt data
  • dec ( cipherPtr keyPtr -- plainPtr ) — Decrypt data
  • encAuth ( dataPtr keyPtr -- cipherPtr ) — Authenticated encryption
  • encS ( dataPtr keyPtr -- cipherPtr ) — Schnorr encryption
  • decS ( cipherPtr keyPtr -- plainPtr ) — Schnorr decryption
  • encAuthS ( dataPtr keyPtr -- cipherPtr ) — Schnorr authenticated encryption
  • ultimium ( dataPtr -- hashPtr ) — Compute Ultimium cryptographic hash
  • addr ( -- addrPtr ) — Get current domain address
  • checkaddr ( addrPtr -- flag ) — Validate address format
  • getNonce ( -- nonce ) — Get transaction nonce
  • nonce ( -- nonce ) — Push nonce value onto stack

State Domain and Navigation

  • cd ( -- ) — Change directory / navigate to state domain
  • ls ( -- ) — List directory contents
  • dir ( -- ) — List directory (alias for ls)
  • mkdir ( -- ) — Create directory in state domain
  • mkdirex ( ptr -- ) — Create directory (explicit pointer)
  • cat ( -- ptr ) — Read file contents from state domain
  • head ( -- ) — Read first lines of file
  • tail ( -- ) — Read last lines of file
  • write ( -- ) — Write data to file
  • touch ( -- ) — Create file or update timestamp
  • rm ( -- ) — Remove file
  • rmEx ( ptr -- ) — Remove file (explicit pointer)
  • csd ( -- ) — Change state domain
  • csds ( -- ) — List child state domains
  • getsd ( -- ptr ) — Get current state domain path
  • ffind ( -- ptr ) — Find file in directory tree
  • less ( -- ) — Paged output display

Variables and State

  • getVar ( -- ptr ) — Read variable by inline name
  • getVarEx ( namePtr domainPtr -- valuePtr ) — Read variable (explicit name and domain)
  • setvar ( -- ) — Write variable (inline name and value)
  • setVarEx ( value isHidden name/path -- ) — Write variable to current domain. The domain is determined by the current cd context, not passed as a parameter. The isHidden flag (0=visible, 1=hidden) controls whether the variable appears in ls listings.
  • flag ( -- ) — Set or get flag variable
  • flagEx ( -- ) — Flag variable (explicit)
  • setMeta ( -- ) — Set metadata on file or variable
  • setMetaEx ( -- ) — Set metadata (explicit)
  • getMeta ( -- ptr ) — Get metadata from file or variable

Value Transfer (GNC)

  • send ( -- ) — Send GNC to address (inline: address amount)
  • sendEx ( sendFlags targetAddr amount -- ) — Send GNC (explicit). For simple transfers, sendFlags=0 is the default. Inside BT/CT, the inline send command auto-generates flags and security tokens.
  • balance ( -- ptr ) — Query domain balance
  • sacrifice ( -- ) — Burn/sacrifice GNC (inline amount)
  • sacrificeEx ( amountPtr -- ) — Burn/sacrifice GNC (explicit)
  • xvalue ( -- value ) — Get value attached to current transaction
  • gotGNC ( -- flag ) — Check if GNC was received in this transaction

Identity and Registration

  • getID ( -- idPtr ) — Get domain identity token
  • genIDEx ( -- ) — Generate identity token
  • regid ( -- ) — Register domain identity or nickname
  • getpub ( -- pubPtr ) — Get domain public key (inline domain)
  • getpubX ( domainPtr -- pubPtr ) — Get domain public key (explicit)
  • whoami ( -- ) — Display current identity information
  • who ( -- ) — Display user information

Token Pools

  • genPool ( -- ) — Generate token pool interactively [T]
  • genPoolEx ( -- ) — Generate token pool (explicit) [T]
  • regPool ( -- ) — Register token pool on-chain
  • regPoolEx ( dataPtr -- ) — Register pool (explicit data pointer)
  • getPool ( -- ptr ) — Query pool information
  • getPoolEx ( idPtr -- ptr ) — Query pool (explicit ID pointer)
  • xTT ( -- ) — Process transmission token transfer
  • xTTex ( -- ) — Process transmission token (explicit)

Permissions and Access Control

  • setfacl ( -- ) — Set file access control list
  • setfaclEx ( -- ) — Set ACL (explicit)
  • chown ( -- ) — Change file/directory ownership
  • chownEx ( -- ) — Change ownership (explicit)
  • getfacl ( -- ) — Get access control list

Smart Contract Execution

  • call ( -- ) — Call smart contract at inline path
  • callEx ( pathPtr -- ) — Call smart contract (explicit path)
  • callTransfer ( -- ) — Call smart contract with value transfer
  • callTransferEx ( valuePtr pathPtr -- ) — Call + transfer (explicit)
  • scex ( -- ) — Smart contract execute
  • evalGPP ( -- ) — Evaluate GridScript++ source file

Transaction Formulation

  • BT ( -- ) — Begin Transaction thread [T]
  • CT ( -- ) — Commit Threads — compile, sign, and submit [T]
  • AT ( -- ) — Abort current Thread
  • BC ( -- ) — Begin Code (raw code accumulation mode) [T]
  • EC ( -- ) — End Code [T]
  • GL ( -- ) — Get accumulated code Lines [T]
  • insert ( -- ) — Insert code into thread [T]
  • SC ( -- ) — Set Code at location [T]
  • cc ( -- ) — Compile and store code [T]
  • BTR ( -- ) — Begin Transaction Raw [T]

Thread Management

  • RT ( -- ) — Resume Thread [T]
  • ST ( -- ) — Suspend Thread [T]
  • PT ( -- ) — Print Thread (show accumulated code) [T]
  • FT ( -- ) — Focus Thread — switch to thread by ID [T]

Registers

  • getReg ( regNum -- value ) — Read value from numbered register
  • rr ( -- ) — Clear all registers (destructive reset)
  • rs ( -- ) — Clear all stacks (destructive reset)

⚠️ Note: Registers are read-only from user GridScript code. The VM sets registers internally during execution. Use getReg to read register values. Do not confuse rr/rs with register read/write — they are destructive reset commands.

ERG Management

  • erg ( -- ) — ERG management interface [T]
  • ergusage ( -- ) — Display current ERG usage
  • ergs ( -- ) — Display ERG statistics

Key Management

  • setKey ( -- ) — Set active private key [T]
  • getKey ( -- keyPtr ) — Get current private key pointer [T]
  • setKeyID ( -- ) — Set active key by ID [T]
  • keychain ( -- ) — List stored keys [T]
  • genKey ( -- ) — Generate new key interactively [T]
  • getCredentials ( -- credPtr ) — Get session credentials [T]
  • genid ( -- ) — Generate identity interactively [T]

Blockchain and Consensus

  • getChain ( -- ) — Get blockchain information [T]
  • getNextID ( -- idPtr ) — Get next block ID [T]
  • commit ( -- ) — Commit pending state changes [T]
  • stepBack ( -- ) — Step back one block [T]
  • setPerspective ( hashPtr -- ) — Set state perspective to specific block [T]
  • perspective ( -- hashPtr ) — Get current perspective hash [T]
  • startFlow ( -- ) — Start execution flow [T]
  • chain ( -- ) — Blockchain operations [T]
  • resync ( -- ) — Resynchronize blockchain state [T]

State Updates

  • update ( -- ) — Trigger state update
  • update1 ( -- ) — State update phase 1
  • update2 ( -- ) — State update phase 2

Network and Node Management

  • stat ( -- ) — Display node statistics [T]
  • vt ( -- ) — View active threads [T]
  • nstart ( -- ) — Start network service [T]
  • nstop ( -- ) — Stop network service [T]
  • npause ( -- ) — Pause network service [T]
  • tstart ( -- ) — Start task [T]
  • tstop ( -- ) — Stop task [T]
  • sct ( -- ) — Set commit target [T]
  • sync ( -- ) — Synchronize state [T]
  • gct ( -- ) — Get commit target [T]
  • cct ( -- ) — Clear commit target [T]
  • net ( -- ) — Display network information [T]
  • route ( -- ) — Display routing information [T]
  • firewall ( -- ) — Firewall management [T]
  • proxy ( -- ) — Proxy configuration [T]
  • ifconfig ( -- ) — Network interface configuration [T]
  • hotstart ( -- ) — Hot restart node [T]

System Information

  • time&date ( -- sec min hr day mon yr ) — Push local time and date components
  • utctime&date ( -- sec min hr day mon yr ) — Push UTC time and date components
  • time ( -- ) — Display current time [T]
  • info ( -- ) — Display domain information
  • unused ( -- n ) — Push amount of unused data space
  • getResult ( -- result ) — Get result of last execution
  • getTaskStatus ( -- status ) — Get status of current task
  • version ( -- ) — Display GridScript version [T]

Admin and System

  • shutdown ( -- ) — Shutdown node (requires admin) [T]
  • bye ( -- ) — Exit terminal session [T]
  • sudo ( -- ) — Elevate to admin privileges [T]
  • logmein ( -- ) — Login to node [T]
  • backup ( -- ) — Backup node data [T]
  • overwatch ( -- ) — Overwatch monitoring operations
  • VMMaintenance ( -- ) — Virtual machine maintenance operations

Mining and Performance

  • calcPerf ( -- ) — Calculate node performance [T]
  • getworkersStat ( -- ) — Get worker statistics [T]
  • genPow ( -- ) — Generate proof of work [T]
  • gettasks ( -- ) — List active tasks [T]
  • getTaskResult ( -- ) — Get task result [T]

Transaction History

  • receipts ( -- ) — View transaction receipts [T]
  • rec ( -- ) — View receipt details [T]
  • tx ( -- ) — View transaction history [T]
  • transactions ( -- ) — View transaction history (alias) [T]
  • diff ( -- ) — View state diff between blocks [T]
  • warnings ( -- ) — View system warnings [T]
  • market ( -- ) — Display market information [T]

Bytecode Compilation

  • compiler ( -- ) — Compile GridScript source to bytecode [T]
  • compilerEx ( sourcePtr -- bytecodePtr ) — Compile to bytecode (explicit) [T]

Polls

  • poll ( -- ) — Create or manage on-chain polls
  • pollEx ( -- ) — Poll operations (explicit parameters)

Miscellaneous

  • updateUI ( -- ) — Trigger UI refresh [T]
  • debugUI ( -- ) — Debug UI state [T]
  • export ( -- ) — Export data [T]
  • import ( -- ) — Import data [T]
  • nick ( -- ) — Set user nickname [T]
  • wall ( -- ) — Broadcast wall message [T]
  • snake ( -- ) — Snake game 🐍 [T]
  • gridcraft ( -- ) — Gridcraft game [T]
  • hexi ( -- ) — Hex inspector [T]
  • context ( -- ) — Context operations (API gateway) — the GridScript-side handler that powers the Blockchain Explorer API. Supports subcommands like -c searchBlockchain, -c getBlocks, -c getDomainInfo, etc. Automatically consumes mkfilter results from the stack. [T]
  • cstorage ( -- ) — Cloud storage operations [T]
  • txconfig ( -- ) — Transaction configuration [T]
  • grid ( -- ) — Grid network operations [T]
  • scm ( -- ) — Source control management [T]
  • gts ( -- ) — Get thread status [T]
  • mkfilter ( -- ptr ) — Create a search filter object for blockchain queries. Supports flags: -tx, -blocks, -domains, -addresses, -all, and arbitrary property filters via -flag "name" "value". Pushes base58Check-encoded filter to stack for automatic use with context. [T]
  • pause ( -- ) — Alias for ST (Suspend Thread) — pause the current thread [T]

ANS Forth File I/O

GridScript implements the ANS Forth file access wordset for local file operations on the GRIDNET Core node. These are terminal-only commands — they operate on the node’s local filesystem, not the decentralized state trie.

  • open-file ( c-addr u fam -- fileid ior ) — Open a file. c-addr u is the filename string, fam is the file access mode
  • create-file ( c-addr u fam -- fileid ior ) — Create a new file (or truncate existing)
  • close-file ( fileid -- ior ) — Close an open file
  • read-file ( c-addr u fileid -- u2 ior ) — Read up to u bytes into buffer at c-addr
  • read-line ( c-addr u fileid -- u2 flag ior ) — Read a line from file
  • read-char ( fileid -- char ior ) — Read a single character
  • write-file ( c-addr u fileid -- ior ) — Write u bytes from buffer to file
  • write-line ( c-addr u fileid -- ior ) — Write a line to file (appends newline)
  • write-char ( char fileid -- ior ) — Write a single character to file
  • flush-file ( fileid -- ior ) — Flush file buffers to disk
  • delete-file ( c-addr u -- ior ) — Delete a file by name
  • rename-file ( c-addr1 u1 c-addr2 u2 -- ior ) — Rename a file
  • include-file ( fileid -- ) — Evaluate GridScript source from a file
  • r/o ( -- fam ) — Read-only file access mode
  • r/w ( -- fam ) — Read-write file access mode
  • w/o ( -- fam ) — Write-only file access mode

Important distinction: These file I/O words operate on the local filesystem of the GRIDNET Core node, not on the decentralized state trie. For decentralized file operations (which are stored on-chain), use the DFS commands: cat, write, touch, rm, mkdir, ls. The ANS file words are useful for node administration tasks, log analysis, and scripting — not for dApp data storage.

Thread Aliases

GridScript provides alternative names for thread management commands that may feel more natural to developers coming from SQL or other paradigms:

  • begin — Alias for BT (Begin Thread)
  • commit — Alias for CT (Commit Threads)
  • abort — Alias for AT (Abort Thread)
  • pause — Alias for ST (Suspend Thread)

So the following two sequences are equivalent:

\ Traditional GridScript style:
BT
cd /MyDomain
send Recipient 1000000000000000000
CT

\ SQL-inspired alias style:
begin
cd /MyDomain
send Recipient 1000000000000000000
commit

Opcode Architecture Note

Commands are assigned opcodes sequentially from BASE_OPCODE_ID = 9. Reserved IDs 1–5 handle literal types (unsigned, signed, double, user-defined, string). IDs 6–8 are immediate words (;, does>, immediate). The built-in primitives dup, swap, over, and rot are handled directly by the interpreter loop and do not consume opcodes from the main command array. The ordering of codewords is immutable — new commands are always appended — because reordering would break all existing compiled bytecode stored on the blockchain.

Inline Arguments — Command-Line-Style Parameters

Many GridScript codewords accept inline arguments parsed directly from the input stream at execution time, rather than consuming values from the stack. These look like command-line flags and parameters:

\ Inline arguments are parsed from the input stream, not the stack:
ls -l -help              \ -l and -help are inline parameters to ls
erg -setbid 100          \ -setbid and 100 are inline parameters to erg
chain -getblocks -page 1 -size 20    \ Multiple inline flags with values
setfacl file1 -m u:lisa:w            \ Unix-like ACL syntax as inline params
cd /MyDomain                          \ The path is an inline parameter to cd
send RecipientAddr 1000000000000000000   \ Address and amount are inline params

Each codeword declaration specifies an inlineParamsCount value indicating how many inline parameters it expects. The codeword reads these from the input stream via getInlineParamVal() at execution time.

Inline vs Explicit (Ex) Forms

During transaction formulation (BT...CT), inline commands are automatically converted to their explicit (Ex) stack-based forms for bytecode serialization. The Ex form is what gets compiled and stored on-chain:

Inline (Terminal) Explicit (Bytecode) Stack Effect
cd /path "/path" cdEx ( pathPtr -- )
getVar 'name' "name" "domain" getVarEx ( namePtr domainPtr -- valuePtr )
setvar 'k' 'v' "v" 0 "k" setVarEx ( value isHidden name -- )
send addr amount 0 "addr" amount sendEx ( sendFlags targetAddr amount -- )
call /path "/path" callEx ( pathPtr -- )
rm filename "filename" rmEx ( ptr -- )
mkdir dirname "dirname" mkdirex ( ptr -- )

This dual-form design means developers can use natural, readable inline syntax in the terminal while the system automatically produces the compact stack-based form for on-chain execution.

Built-in Man Pages — Discover Commands with -help

Many GridScript codewords have built-in help pages accessed via the -help inline flag. This is a key developer discovery tool — when unsure about a command’s syntax, always try command -help first:

ls -help            \ File listing options and syntax
erg -help           \ ERG management subcommands
setfacl -help       \ Full ACL syntax with examples
market -help        \ Market information queries
rm -help            \ File removal options
cat -help           \ File content display options
peers -help         \ Peer/network information
compiler -help      \ Compilation options and flags

Help pages display formatted, colored output organized into [Description], [Syntax], and [Examples] sections. They are built into the codeword implementations and are always available in terminal mode.

Domain (Account) Creation

Domains (accounts) in GRIDNET OS follow the same creation model as Ethereum: a domain is created automatically when assets are first dispatched to it. There is no explicit “create account” transaction needed. When you send GNC to a new address, the receiving domain is created in the state trie as a side effect of the transfer:

\ This creates the domain if it doesn't exist yet:
BT
cd /YourDomain
send NewDomainAddress 1000000000000000000    \ 1 GNC — domain is created automatically
CT

A domain address is derived from the public key (via base58Check encoding). Once created, the domain persists in the Merkle Patricia Trie and can hold variables, files, smart contracts, identity tokens, and sub-domains.

Decentralized File System (DFS) API

GRIDNET OS includes an extremely sophisticated Decentralized File System with Unix-like file operations and fine-grained access control. Files are stored across the decentralized network with full ACL management.

File Operations

\ Familiar Unix-like commands:
ls                  \ List directory contents
ls -l               \ Long listing format
cd /domain/path     \ Navigate to path
mkdir mydir         \ Create directory
touch myfile        \ Create file or update timestamp
cat myfile          \ Read file contents
head myfile         \ First lines of file
tail myfile         \ Last lines of file
write myfile        \ Write to file
rm myfile           \ Remove file
ffind filename      \ Search for file in directory tree
less myfile         \ Paged output display

Access Control Lists (ACLs)

The setfacl / getfacl codewords provide Unix-like ACL management for decentralized files:

\ Grant user lisa write access to file1:
setfacl file1 -m u:lisa:w

\ Display ACL entries for a file:
getfacl file1

\ Clear all extended ACL entries:
setfacl file1 -b

\ Change file ownership:
chown file1 newOwnerAddress

\ Full syntax reference:
setfacl -help       \ Displays detailed help with all options

The ACL system supports user-level permissions (read, write, execute) on files and directories stored in the decentralized state trie. This enables multi-user access control in a fully decentralized context — no central authority manages permissions.

V-B. The Data Type System — BigInt, BigSInt, and BigFloat

GridScript’s data stack is not limited to fixed-size integers. The VM tracks the data type of every value on the stack via the parallel meta stack. Understanding these types is critical for cryptocurrency operations where values exceed 64-bit limits.

Supported Data Types

Type Description Use Case
signedInteger 64-bit signed integer General arithmetic, loop counters, flags
unsignedInteger 64-bit unsigned integer Addresses, sizes, bitwise operations
BigInt Arbitrary-precision unsigned integer GNC values (1 GNC = 1018 attoGNC), balances, large computations
BigSInt Arbitrary-precision signed integer Signed large values, balance differences
BigDouble / doublee Arbitrary-precision and IEEE 754 floating point Decimal calculations, ratios, percentages
pointer Memory address (points to allocated region) String data, binary data, allocated memory blocks

Automatic Type Promotion

When arithmetic operations encounter mixed types, the VM automatically promotes values to the wider type. For example, adding a signedInteger to a BigInt promotes the integer to BigInt before performing the operation. The result inherits the promoted type. This is particularly important for GNC value calculations — the send and sendEx commands work with BigInt values internally, so values like 1000000000000000000 (1 GNC) are automatically handled as arbitrary-precision integers.

Type Inspection

The .s command shows the data type of each stack item in its DataType column. The .m command shows memory regions for pointer types. Together, these debugging tools let you verify that your values have the expected types at every step:

\ Push a regular integer — type is signedInteger
42 .s
\ # | Value | DataType
\ 0 | 42    | signedInteger

\ Push a GNC value — automatically BigInt
1000000000000000000 .s
\ # | Value              | DataType
\ 0 | 1000000000000000000| BigInt

\ String literal — type is pointer
42 "hello" .s
\ # | Value  | DataType
\ 0 | 0x...  | pointer
\ 1 | 42     | signedInteger

V-C. Blockchain Search Filters — The mkfilter System

The mkfilter codeword creates structured search filter objects for querying the blockchain via the context command. This is the programmatic interface behind the Blockchain Explorer’s search functionality — and it is available to any developer building dApps that need to query blockchain data.

Basic Usage

\ Create a filter for transaction searches:
mkfilter -tx -flag "tx_typeName" "sacrifice"

\ Use it with context to search the blockchain:
context -c searchBlockchain -search ""

\ The filter is automatically detected and consumed from the stack

Standard Flags (Entity Types)

  • -tx — Include transactions (default if no flags specified)
  • -blocks — Include blocks
  • -domains — Include domains/accounts
  • -addresses — Include addresses
  • -all — Include all entity types

Arbitrary Property Filters

Use -flag "name" "value" to filter by specific properties. You can specify multiple flags — same-name flags use OR logic (match any).

Transaction Filters (prefix: tx_)

  • tx_type, tx_typeName — Transaction type (numeric or name)
  • tx_sender, tx_receiver — Sender/receiver address
  • tx_value — Transfer value
  • tx_verifiableID — Verifiable transaction ID
  • tx_sacrificedValue — Sacrificed (staked) amount
  • tx_nonce, tx_result, tx_resultTxt — Nonce, result code, result text
  • tx_auxContractPath — Smart contract path
  • tx_auxTokenPoolID — Token pool ID
  • tx_auxFriendlyID — Human-readable identifier (nickname)
  • tx_auxValue — Auxiliary value

Block Filters (prefix: block_)

  • block_blockID, block_type, block_height, block_keyHeight
  • block_solvedAt, block_minerID, block_parentID
  • block_ergUsed, block_ergLimit, block_nonce
  • block_blockReward, block_receiptsCount, block_transactionsCount

Domain Filters (prefix: domain_)

  • domain_domain, domain_balance, domain_lockedBalance
  • domain_txCount, domain_txTotalReceived, domain_txTotalSent
  • domain_perspective, domain_perspectivesCount
  • domain_publicKey, domain_nickname, domain_stake

Transaction Types Reference

ID Name ID Name
0 transfer 7 tokenPoolRegistration
1 blockReward 8 identityRegistration
2 offChain 9 permissionChange
3 offChainCashOut 10 smartContractCall
4 contract 11 valueContractCall
5 genesisReward 12 gridScriptPP
6 sacrifice 13 arbitraryExecution

Note: Transactions may have multiple types simultaneously — a transaction containing both sendEx and xTTex carries both transfer and offChainCashOut type flags. These appear as “Hybrid” transactions in the Explorer.

Worked Examples

\ 1. Find all sacrifice transactions
mkfilter -tx -flag "tx_typeName" "sacrifice"
context -c searchBlockchain -search ""

\ 2. Find identity registrations by nickname "alice"
mkfilter -tx -flag "tx_typeName" "identityRegistration" -flag "tx_auxFriendlyID" "alice"
context -c searchBlockchain -search ""

\ 3. Find blocks mined by a specific address
mkfilter -blocks -flag "block_minerID" "1ABCxyz..."
context -c searchBlockchain -search ""

\ 4. Find domains by nickname
mkfilter -domains -flag "domain_nickname" "bob"
context -c searchBlockchain -search ""

\ 5. Find smart contract calls to a specific contract
mkfilter -tx -flag "tx_typeName" "smartContractCall" -flag "tx_auxContractPath" "/1ABCxyz.../app.gs"
context -c searchBlockchain -search ""

\ 6. Multi-type search (OR logic) — find both transfers and cashouts
mkfilter -tx -flag "tx_typeName" "transfer" -flag "tx_typeName" "offChainCashOut"
context -c searchBlockchain -search ""

\ 7. Keep filter for reuse across multiple queries
mkfilter -tx -flag "tx_typeName" "sacrifice"
context -c searchBlockchain -search "" -keepfilter
context -c searchBlockchain -search "" -keepfilter
drop    \ Remove filter when done

Filter Stack Integration

The filter is pushed onto the data stack as a base58Check-encoded pointer. The context command automatically detects and consumes filters from the stack. Use -keepfilter with context to retain the filter for reuse. Use -nostack to disable auto-detection entirely. To manually discard a filter, use drop.

VI. Practical Examples — GridScript in Action

Example 1: Value Transfer from the Terminal

\ Step 1: Set ERG parameters
erg -setbid 100
erg -setlimit 50000

\ Step 2: Formulate and commit the transaction
BT
cd /YourDomainAddress
send RecipientAddress 1000000000000000000
CT

\ Step 3: Verify (after block confirmation)
balance 'RecipientAddress'
.

Example 2: Deploying a Smart Contract

BT
cd /YourDomain/contracts
BC
\ Simple counter contract
cd /YourDomain
getVar 'counter'
1 +
0 "counter" setVarEx
EC
CT

\ Later, call it:
BT
cd /YourDomain
call /YourDomain/contracts
CT

Example 3: Deploying an Identity Token

Identity tokens establish your presence in the GRIDNET OS ecosystem. The process involves a sacrificial transaction (staking GNC) followed by identity registration:

\ Step 1: Sacrifice GNC for the identity stake
BT
cd /YourDomain
sacrifice '100000000000000000000'    \ Stake 100 GNC
CT

\ Step 2: After confirmation, register the identity
BT
cd /YourDomain
regid                                 \ Register identity with nickname
CT

The Wallet dApp’s Identity Token Registration Wizard automates this entire multi-stage process — including DPT pre-validation, BER-encoded identity token creation in JavaScript, and progressive commit monitoring — making it accessible without understanding the underlying GridScript.

Example 4: Querying Blockchain State

\ View domain information
info 'DomainAddress'

\ Check balance
balance 'DomainAddress'
.

\ View transaction history
tx DomainAddress

\ Get blockchain status
chain

\ View receipts
receipts DomainAddress

VII. GridScript in the JavaScript Ecosystem — The Browser Bridge

UI dApp Architecture — The Two-Part Model

Understanding how GRIDNET OS UI dApps work is essential before diving into the JavaScript APIs. A typical UI dApp has two distinct parts:

  1. JavaScript part — Executes in the web browser. Handles the user interface, DOM manipulation, event handling, and interacts with GRIDNET OS APIs through the CVMContext JavaScript singleton. This is what most dApp developers work with day-to-day. All GRIDNET OS API calls are JavaScript constructs.
  2. GridScript part — Executes on the GridScript VM on remote GRIDNET Core nodes. This is the low-level layer used when truly needed: value transfers, smart contract deployment and invocation, state modifications, identity operations. GridScript runs on the decentralized network — deterministically, verifiably, across every node.

The key insight: most GRIDNET OS API calls are JavaScript. Using GridScript directly is a low-level approach for when you need state changes committed to the blockchain. The JavaScript layer handles data retrieval, UI rendering, event subscriptions, and user interaction. The GridScript layer handles the things that must be deterministic and consensus-verified.

For hands-on tutorials on building UI dApps, see:

The CVMContext Singleton

📖 Comprehensive Reference: CVMContext method signatures, event types, and the full JavaScript API are documented in the VMContext Documentation article. The Blockchain Explorer API Documentation covers the query API that CVMContext exposes for data retrieval. This section explains how CVMContext connects to the GridScript layer — for the full API reference, see those articles.

The context codeword in GridScript is what powers the Blockchain Explorer API under the hood — it is the GridScript-side handler that CVMContext calls into when JavaScript code makes data queries. When you use the Blockchain Explorer API from JavaScript, your request is ultimately executed as GridScript on a remote node via the context codeword.

CVMContext.getInstance() manages:

  • WebSocket connection to GRIDNET Core nodes with automatic reconnection
  • VM Meta-Data protocol parsing and generation via CVMMetaParser/CVMMetaGenerator
  • Process/Thread management — each UI dApp gets a CProcess that can spawn CThreads for background GridScript execution on the remote node
  • Request ID correlation — every outgoing request gets a unique ID; responses carry the same ID back, enabling concurrent asynchronous operations
  • Event system — listeners for VM metadata, DFS messages, GridScript results, terminal data, VM state changes, and commit state transitions. This is the dispatch mechanism through which CVMContext notifies individual UI dApp instances — each dApp registers its callbacks with a window ID, and CVMContext routes incoming BER responses to the appropriate listeners. Multiple dApps can listen simultaneously; a single incoming response may trigger callbacks in multiple UI dApp instances

How a dApp Retrieves Data

When the Blockchain Explorer needs to display recent blocks:

// 1. JavaScript constructs the GridScript request
const generator = new CVMMetaGenerator();
const reqID = CVMMetaGenerator.genReqID();
generator.addRAWGridScriptCmd(
'chain -getblocks -page 1 -size 20 -sort height',
reqID, processID, vmID
);

// 2. BER-encode and send through WebSocket
const berData = generator.finalize();
vmContext.send(berData);

// 3. Register callback for this specific request
vmContext.addVMMetaDataListener((sections) => {
for (const section of sections) {
for (const entry of section.entries) {
if (entry.reqID === reqID) {
// 4. Decode BER response and render
const blockDesc = CBlockDesc.instantiate(entry.data);
renderBlockRow(blockDesc);
}
}
}
}, windowID);

This pattern — construct, encode, send, correlate, decode, render — is the heartbeat of every UI dApp in GRIDNET OS. The Terminal dApp uses it. The Wallet uses it. The Explorer uses it. The File Manager uses it. Even the Snake game uses it.

In-Browser GridScript Compilation

For committed transactions, the browser goes further. GridScriptCompiler.js compiles GridScript source code directly into bytecode in the browser:

  • V1 bytecodes (legacy): raw opcode sequence
  • V2 bytecodes (current): version byte + 32-byte hash + opcodes — enabling integrity verification
  • Opcodes ≤ 127 use single-byte encoding; opcodes > 127 use two-byte encoding with a high-bit flag

The Wallet dApp uses this to compile, sign (via Ed25519 ECC), and dispatch transactions entirely from within the browser — no server involvement. CTransaction.js handles BER serialization of the transaction structure, CKeyChainManager provides HD wallet key derivation, and the compiled bytecode passes all pre-validation tests on GRIDNET Core including code integrity and signature verification.

The Compiler and Bytecode Format — From Source to On-Chain Execution

Understanding how GridScript source code becomes executable bytecode is essential for advanced development. The compilation pipeline operates identically in two implementations — the C++ reference compiler inside GRIDNET Core and the JavaScript compiler (GridScriptCompiler.js) running in the browser — producing bit-identical output.

The compiler Codeword — Terminal Developer Tool

The compiler codeword is a terminal-only developer utility for compiling GridScript source to bytecode, generating transactions, and testing code without committing anything to the blockchain:

\ Basic compilation — outputs base64 bytecode:
compiler "cd /MyAddress"
\ Output: ✓ Compilation Successful, bytecode size, V2 format

\ Compile and generate an unsigned transaction:
compiler "send 1BKGXMj441G5iWuQyxS7Bpr4bxr9vAvXWy 2000000000000000000" -tx

\ Compile, generate, and sign a transaction:
compiler "send Target 1000" -tx -sign <base58_private_key>

\ Generate a QR code of the bytecode (for mobile scanning):
compiler "cd /MyAddress" -qr

\ Generate a QR code of the signed transaction:
compiler "send Target 1000" -tx -sign <key> -qrtx

\ Built-in help:
compiler -help

Stack output after compilation: On success, the stack contains ( [tx_ptr if -tx] bytecode_ptr 1 ). On failure: ( error_msg_ptr 0 ). This enables programmatic reuse — you can compile bytecode and pass it to other operations.

ERG cost: 10 base + sourceLength/100. Terminal-only (onlyFromTerminal = true, allowedInKernelMode = false).

The V2 Bytecode Format

GridScript uses two bytecode formats. All new compilation produces V2; the decompiler supports both.

V1 (Legacy) — Raw opcodes with no integrity verification:

┌──────────┬──────────┬──────────┬──────────┐
│ OPCODE_1 │ PARAMS_1 │ OPCODE_2 │ PARAMS_2 │ ...
└──────────┴──────────┴──────────┴──────────┘

V2 (Current) — Version header + SHA256 keyword hash chain for integrity verification:

┌──────────────┬───────────────────────────────┬──────────┬──────────┐
│ VERSION_BYTE │ 32-BYTE SHA256 KEYWORD HASH   │ OPCODE_1 │ PARAMS_1 │ ...
│ (≥ 192)      │ (integrity verification)      │          │          │
└──────────────┴───────────────────────────────┴──────────┴──────────┘
 1 byte          32 bytes                        opcodes...

The version byte is always ≥ 192 (if the raw version number is < 192, the formula 192 + (version % 64) is applied). The decompiler detects V1 vs V2 by checking: byte[0] < 192 → V1, byte[0] ≥ 192 → V2.

Opcode Encoding

Opcodes are assigned sequentially from BASE_OPCODE_ID = 9. The encoding uses a compact scheme:

Single-byte encoding (opcode ID ≤ 127):

┌──────────┐
│ ID_BYTE  │    Example: 'cd' = opcode 148... wait, that's > 127
└──────────┘    Example: '+' = opcode 24 → [0x18]

Two-byte extended encoding (opcode ID > 127):

┌───────────────────┬──────────┐
│ HIGH_BYTE | 0x80  │ LOW_BYTE │
└───────────────────┴──────────┘
 Bit 7 set on first byte; bytes in swapped order (little-endian with flag)

For example, cd at opcode 162: low byte = 0xA2, high byte = 0x00. After swap and flag: [0x80, 0xA2].

Length Encoding (for inline parameters)

When a codeword has inline parameters, the parameter data length is encoded after the opcode:

Simple (length ≤ 127): single byte [LENGTH]

Extended (length > 127): [NUM_LENGTH_BYTES | 0x80][BYTE_0][BYTE_1]... where length bytes are little-endian.

Parameters that are addresses (base58-encoded) are decoded to their binary form before storage. A base58 address like /144c2Aof5t3GDUTv8x9mPorpzMLhkFQLWp becomes 21 bytes of binary, not 35 bytes of ASCII.

The Keyword Hash Chain — Bytecode Integrity

V2 bytecode includes a 32-byte SHA256 hash that verifies the integrity of the compiled keyword sequence. The chain is computed during compilation:

1. Initialize: hash = SHA256(GRIDSCRIPT_IMAGE_INIT_STRING)
2. For each keyword compiled:
   hash = SHA256(hash + keyword_name_bytes)
3. Final hash is stored in the V2 header (bytes 1–32)

\ During decompilation, the same chain is recomputed.
\ If the final hash doesn't match the header → bytecode is corrupt or tampered.

This ensures that bytecode cannot be modified after compilation without detection — a critical property for smart contracts stored on the blockchain.

In-Browser Compilation — Full C++ Parity

The JavaScript compiler (GridScriptCompiler.js) achieves bit-identical output with the C++ reference compiler. This is what enables the Wallet dApp to compile, sign, and submit transactions entirely from the browser:

// Async compilation API (uses Web Crypto for SHA256 hash chain)
const result = await compiler.compile(sourceCode);
// Returns: { success: boolean, bytecode: Uint8Array|null, errors: string[] }

// Async decompilation (supports both V1 and V2)
const result = await compiler.decompile(bytecode);
// Returns: { success: boolean, source: string|null, errors: string[] }

// Full round-trip guarantee:
// compile(JS) → decompile(C++) → identical source
// compile(C++) → decompile(JS) → identical source

The JavaScript compiler uses the Web Crypto API (crypto.subtle.digest('SHA-256', data)) for the keyword hash chain, making compilation asynchronous. It handles all encoding details identically to C++: extended opcode encoding for IDs > 127, extended length encoding for parameters > 127 bytes, base58 address decoding, and V2 header generation.

The end-to-end pipeline in the Wallet dApp: GridScript source → GridScriptCompiler.js compiles to V2 bytecode → CTransaction.js wraps in BER-serialized transaction → CKeyChainManager signs with Ed25519 ECC → Web Workers handle encoding off the main thread → dispatched through onion-routed encrypted WebSocket. No server touches the code at any point.

VIII. Deploying Your UI dApp On-Chain

Everything described so far — the SOA pipeline, CVMContext, BER encoding, GridScript compilation in the browser — converges here: building and deploying a UI dApp that lives on the blockchain. No server. No app store. No gatekeeper. Your code is committed to the Decentralized File System (DFS), replicated across the network, and available to every user globally. It cannot be censored, taken down, or altered by anyone other than the deploying entity. This is what decentralized application deployment actually looks like.

The .app Package Format

A GRIDNET OS UI dApp is a single JavaScript file with the .app extension. It contains one ES6 class that extends CWindow — the base window class provided by the GRIDNET OS desktop environment — and exports it as the default export. That’s it. One file, one class, one export.

The package must declare a PackageID that follows the reverse-domain convention and begins with org.gridnetproject.UIdApps. — for example, org.gridnetproject.UIdApps.MyApp. The class implements three required identification methods:

  • getPackageID() — Returns the unique package identifier string
  • getIcon() — Returns an emoji or icon character for the desktop and taskbar
  • getDefaultCategory() — Returns the application category (e.g., "Utilities", "Finance", "Games")

If your dApp depends on external libraries, bundle them into the single file using Rollup, Webpack, or any JS bundler. Do not bundle GRIDNET OS system libraries (CVMContext, CWindow, CTools, etc.) — these are provided by the runtime environment and available globally.

Minimal Code Skeleton

export default class MyApp extends CWindow {

    getPackageID()        { return "org.gridnetproject.UIdApps.MyApp"; }
    getIcon()             { return "🚀"; }
    getDefaultCategory()  { return "Utilities"; }

    constructor(params) {
        super(params);
        this.setTitle("My First dApp");
    }

    onReady() {
        // Shadow DOM — fully scoped, no style leakage
        const body = this.getBodyElement();
        body.innerHTML = `
            <style>
                .container { padding: 16px; color: #0f0; font-family: monospace; }
                button { background: #222; color: #0f0; border: 1px solid #0f0;
                         padding: 8px 16px; cursor: pointer; }
                button:hover { background: #0f0; color: #000; }
            </style>
            <div class="container">
                <h2>Hello, GRIDNET OS!</h2>
                <p id="output">Click the button to query the network.</p>
                <button id="btn">Query Balance</button>
            </div>
        `;

        // Event handling — standard DOM APIs inside Shadow DOM
        body.querySelector("#btn").addEventListener("click", () => {
            body.querySelector("#output").textContent = "Querying via CVMContext...";
            // Use CVMContext APIs to interact with the network
        });
    }
}

Every UI dApp runs inside a Shadow DOM — styles and DOM are fully scoped to the window. Your CSS cannot leak into other dApps, and theirs cannot affect yours. The onReady() method is called once the window is initialized and the Shadow DOM is available.

Development Workflow

  1. Start from the template — Clone or download AppTemplate.js / UserDeployable.app from the GRIDNET OS GitHub repository. This is a working skeleton with all required methods already stubbed.
  2. Rename the class — Change the class name and update getPackageID() to your unique identifier.
  3. Set the window title — Call this.setTitle("Your App Name") in the constructor.
  4. Define your UI — Write your HTML and CSS inside onReady() using this.getBodyElement().innerHTML. Everything lives in Shadow DOM.
  5. Set an icon — Return an emoji from getIcon() (displayed on the desktop, taskbar, and app launcher).
  6. Add logic — Use CVMContext for network queries, CTools for utilities, and standard JavaScript for everything else.

Sandbox Testing — Zero Cost

Before committing anything to the blockchain, you can test your dApp for free:

  1. Drag your .app file onto the File Manager window in GRIDNET OS (the browser-based desktop).
  2. The PackageManager automatically analyzes the file — it validates the class structure, checks the PackageID format, and verifies that required methods are present.
  3. Click to run — the dApp launches in a sandboxed window. You can interact with it, test UI behavior, and verify network queries. No GNC is consumed. Nothing is written to the blockchain.

This sandbox cycle — edit, drag, test — is your development loop. Iterate until your dApp works exactly as intended.

Committing to the Network — Permanent Deployment

When your dApp is ready for the world:

  1. Select the .app file in the File Manager.
  2. Click the ⋮⋮⋮ Magic Button — this opens the commit dialog.
  3. Commit to DFS — the file is compiled into a transaction, signed with your private key, and submitted to the network. This costs GNC (the transaction includes the file data, so larger dApps cost more).
  4. Once confirmed in a block, your dApp is permanently stored on the Decentralized File System — replicated across all nodes, globally accessible, censorship-resistant.

Your dApp now exists on-chain. It has no server to go down, no hosting bill to expire, no app store review to pass. It is code on a decentralized state machine, and it will remain there as long as the network exists.

User Installation

Any GRIDNET OS user can install your dApp:

  1. Navigate to your State-Domain (your account’s file hierarchy) using the File Manager or a direct link.
  2. Find the .app file in your published directory.
  3. Click to install — the PackageManager registers it locally.
  4. The dApp’s icon appears on the user’s Desktop and in the App Launcher, ready to use.

Key APIs for dApp Development

GRIDNET OS provides a rich set of JavaScript APIs available to every UI dApp at runtime:

  • CVMContext — The central singleton for all network communication. Data queries, GridScript execution, BER encoding/decoding, event subscriptions. See the VMContext Documentation and Blockchain Explorer API Documentation.
  • CTools — Utility functions for formatting, conversion, and common operations.
  • CSettingsManager — Persistent user preferences and dApp configuration storage.
  • CContentHandler — MIME-type handling, file association, and content rendering.
  • CNetMsg / WebRTC Swarms — Peer-to-peer messaging and real-time communication between dApp instances across the network.
  • DFS (Decentralized File System) — File storage, retrieval, and management on the decentralized network.
  • GridScriptCompiler.js — In-browser GridScript compilation for dApps that need to formulate and submit transactions programmatically.

Further Reading

IX. GridScript++ — The Extended Language

GridScript++ extends GridScript with higher-level constructs. It can be invoked from within GridScript using the evalGPP command:

BT
cd /YourDomain
evalGPP 'myScript.gpp'
CT

GridScript++ provides facilities familiar to developers from languages like JavaScript — including a console.log function for debugging output, cryptographic APIs, and more complex control structures. It represents the evolution of GridScript toward developer accessibility while maintaining the deterministic execution guarantees required by the consensus layer.The relationship between GridScript and GridScript++ mirrors the relationship between assembly language and a high-level language — GridScript provides the raw stack-based instruction set that runs on the VM, while GridScript++ offers a more expressive syntax that compiles down to GridScript operations.

X. The ERG System — Metering Computation in a Trustless World

This is where GridScript most fundamentally departs from its Forth ancestry. In Forth, you type 10 20 + and the processor executes it for free — the only cost is electricity and time. In GridScript, that same 10 20 + consumes 3 ERG: 1 ERG for the literal 10, 1 ERG for the literal 20, 1 ERG for the addition. Every single instruction — every push, every pop, every comparison, every branch — has a price. This is not overhead. It is the mechanism that makes trustless computation possible.

ERG (Execution Resource Gas) is GridScript’s resource metering system, analogous to gas in Ethereum. It prevents infinite loops, denial-of-service attacks, and resource exhaustion in a network where anyone can submit arbitrary code for execution across all nodes.

Every Codeword Has a Price

Each of the 290+ codewords includes a base ERG cost in its declaration. This is the minimum cost — many operations accrue additional costs based on data size or computational complexity.

Category Operations ERG Cost
Arithmetic +, -, *, /, mod 1 ERG base
Stack ops dup, drop, swap, over, rot 1 ERG base
Comparison/Logic <, =, and, or, xor 1 ERG base
State reads getVar, getVarEx, cat, balance 1 ERG base + dynamic data-size cost
State writes setVarEx, write 1 ERG base + dynamic data-size cost
Key generation keygen, keygenS 10 ERG
Signing sign, versig 80 ERG
Signature check checkSig 100 ERG
Encryption enc, dec, encAuth, encAuthS, encS 60–90 ERG + data size (enc, dec, encAuth: 60; encAuthS: 70; encS: 90)
Memory allocation alloc 1 ERG per byte (ERG_COST_PER_RAM_BYTE)
String operations concat, type Proportional to string length
Hashing ultimium 30 ERG
Data space allocation allot 100 ERG base (high cost)

Base costs shown. Some operations add dynamic costs proportional to data size.

The total ERG cost of any operation is: Total = baseERGCost + (memoryBytes × ERG_COST_PER_RAM_BYTE) + computationERG + overheadERG

Bid vs Limit — The Two ERG Parameters

Every transaction requires two ERG parameters, stored in dedicated registers:

  • ERG Bid (REG_KERNEL_ERG_BID, register 46) — The price per ERG unit you are willing to pay. This affects transaction priority: higher bids get processed first when the network is congested. Think of it as the gas price.
  • ERG Limit (REG_KERNEL_ERG_LIMIT, register 47) — The maximum total ERG the transaction may consume. If execution exceeds this limit, the transaction fails and all state changes are rolled back. Think of it as the gas limit.
\ Set ERG parameters before formulating a transaction:
erg -setbid 100         \ Willing to pay 100 per ERG unit (priority)
erg -setlimit 50000     \ Maximum 50,000 ERG total for this transaction

\ Query current settings:
erg -getbid             \ Displays current bid
erg -getlimit           \ Displays current limit

\ Increment (useful for retry with higher values):
erg -incbid 10          \ Increase bid by 10
erg -inclimit 5000      \ Increase limit by 5000

\ Query sandbox execution stats (read-only):
erg -getsandbox         \ Shows ERG consumed, ERG limit, ERG left, overhead estimate

\ Full comprehensive report:
erg                     \ Displays everything

The ERG Report

Calling erg with no parameters displays a comprehensive status report:

╔═══════════════════════════════════════╗
║           ERG STATUS REPORT            ║
╠═══════════════════════════════════════╣
║ Kernel ERG Bid:     100               ║
║ Kernel ERG Limit:   50000             ║
╠═══════════════════════════════════════╣
║ Sandbox ERG Used:   1234              ║
║ Sandbox ERG Limit:  100000            ║
║ Sandbox ERG Left:   98766             ║
║ TX Overhead Est:    500               ║
╚═══════════════════════════════════════╝

The Kernel parameters (Bid/Limit) are what you set — they control the transaction when it executes on-chain during consensus. The Sandbox parameters are the current ephemeral execution context — they show how much ERG your interactive session has consumed so far.

ERG in the Transaction Lifecycle

Understanding when and how ERG is consumed requires tracing the full transaction lifecycle:

  1. Set Bid and Limiterg -setbid 100 and erg -setlimit 50000. These values are stored in registers 46 and 47. If not set, the VM will prompt interactively (inconvenient for automation).
  2. BT — Begin Thread — Creates a sandbox sub-thread. ERG accounting begins for the thread.
  3. Accumulate commands — Each command entered is recorded into the thread’s code buffer. The sandbox tracks estimated ERG consumption as commands execute locally for preview.
  4. CT — Commit Threads — The VM collects code from all ready threads, calculates total ERG: Total = Σ(thread ERG consumed + thread transaction overhead). Verifies total ≤ ERG Limit. Compiles to bytecode, signs, submits.
  5. Network propagation — Transaction propagates to all nodes. The ERG Bid and Limit are included in the transaction header.
  6. Kernel execution — On every node, the bytecode executes in kernel mode. Every instruction deducts ERG from the budget. The VM checks the remaining ERG budget between instructions in the execution loop.
  7. If ERG exhaustedRUNTIME_ERROR("out of ERG by N"). Transaction fails. All state changes are rolled back. The ERG is still consumed (you pay for failed computation).
  8. If successful — State changes committed to the Merkle Patricia Trie. Actual ERG consumed is recorded. Unused ERG is not charged.

How ERG Enforcement Works

Every instruction has an ERG cost. The VM checks the remaining ERG budget before each instruction executes. If your transaction runs out of ERG, it fails immediately and all state changes are rolled back. You pay for the computation consumed up to the failure point, but the blockchain state remains unchanged.

Use erg -getsandbox and ergusage during development to estimate costs before committing transactions. This lets you set an appropriate ERG Limit and avoid failed transactions.

The ERG Excuse Mechanism

REG_EXCUSE_ERG_USAGE (register 45) is a special flag that bypasses all ERG checks when set. It exists for one specific purpose: when a node is replaying blocks that have already been validated by the network (e.g., during synchronization or checkpoint recovery), the code is known to have succeeded previously. Re-checking ERG would be wasteful and could fail if limits have changed. When this register is set, all ERG checks pass through without verifying.

This register is only set by trusted internal code during checkpoint verification. It is never exposed to user-mode GridScript.

Thread ERG Synchronization

When erg is called from a sub-thread and sets values higher than the main thread’s values, it automatically updates the main thread. This ensures consistency across the thread hierarchy during transaction commitment — you don’t need to worry about setting ERG separately for each thread.

ERG Commands Reference

  • erg ( -- ) — Full ERG management interface. Terminal only. Supports subcommands: -setbid, -setlimit, -getbid, -getlimit, -getsandbox, -incbid, -inclimit, -help
  • ergusage ( -- ) — Display current ERG consumption. Terminal-only. To monitor ERG in smart contracts, estimate costs against known limits before operations.
  • ergs ( -- ) — Display ERG statistics.

Best Practices

  1. Estimate before committing — Use erg -getsandbox to check consumption during development. The sandbox ERG reflects what your transaction will cost on-chain.
  2. Set adequate limits — Too low and the transaction fails (wasting your effort). Too high wastes nothing (unused ERG is not charged), but set reasonable bounds.
  3. Minimize memory allocations — Each byte costs 1 ERG. A 1 MB allocation costs 1,048,576 ERG. Allocate only what you need.
  4. Cache state reads — Every getVar call costs 1 ERG base plus dynamic data-size cost. If you need the same variable twice, read it once and dup the result.
  5. Watch loops — A DO...LOOP iterating 10,000 times with a 3-ERG body costs 30,000 ERG. ERG exhaustion inside a loop is a common cause of transaction failure.

Common Pitfalls

  • ERG too low — Transaction fails at consensus. All work is lost. State is rolled back. Always test with erg -getsandbox first.
  • Forgetting transaction overhead — transaction overhead adds to the total. Serialization and signature verification have real costs.
  • Not setting bid/limit before CT — The VM will prompt interactively, which breaks automated workflows. Always set explicitly.
  • Memory-intensive operations — Allocating a large buffer, performing cryptographic operations on it, then discarding it — the ERG cost is the sum of allocation + computation. Plan accordingly.
  • Nested contract calls — Each callEx enters a new execution scope but shares the same ERG budget. Deep call chains can exhaust ERG unexpectedly.

XI. The Power of Reproducible, Verifiable Code

There is a profound idea at the heart of GridScript that transcends the technical details of stack operations and opcode encodings.Every action a user takes in GRIDNET OS — sending value, deploying an identity, creating a file, calling a smart contract — is formulated as GridScript. This GridScript is compiled to bytecode. That bytecode is signed, committed to the blockchain, and executed identically on every node in the network. The state changes are recorded in a Merkle Patricia Trie whose root hash is included in the block header.This means that every user action becomes reproducible, committable, and verifiable code. A value transfer is not a database entry updated by a trusted server — it is a program that executes and produces deterministic results. An identity registration is not a form submission to an authority — it is a cryptographically signed instruction sequence that any node can re-execute and verify.The GridScript VM has 48 registers, three stacks, 256 levels deep each, and a security model with six privilege levels. It compiles to a bytecode format where the opcode order is immutable — new commands are only ever appended, never inserted or reordered, preserving backward compatibility with every contract ever deployed. The codeword registry has grown to over 290 entries across the VM’s lifetime, and not one has ever been moved.This is what it means to build a language for a decentralized state machine. Not a language bolted onto a blockchain as an afterthought, but a language that is the blockchain’s native expression — from the stack operations inherited from Forth, through the transaction formulation threads that make state changes atomic, to the SOA architecture that turns every browser tab into a window onto a decentralized world.GridScript is the language of GRIDNET OS. And GRIDNET OS is, by construction, a machine that cannot lie about what it has done — because everything it has done is written in GridScript, compiled to bytecode, and verified by every node in the network.


GridScript and GRIDNET OS are created by Rafał Skowroński. Development has been conducted with full transparency since 2017 — nearly every line of code conceived, implemented, debugged, and validated on YouTube LIVE. For more information, visit gridnet.org or join the community at talk.gridnet.org.

GRIDNET

Author

GRIDNET

Up Next

Related Posts