Building with SpaceComputer Orbitport: A Guide to Cosmic Randomness in Web3

Building with SpaceComputer Orbitport: A Guide to Cosmic Randomness in Web3

Introduction

True randomness is vital for ensuring fairness and security within decentralized applications (dApps), especially in scenarios involving cryptography and gaming. SpaceComputer Orbitport addresses this critical need uniquely by leveraging satellite technology to offer authentic and tamper-proof randomness and secure compute, through its innovative infrastructure and APIs.

This comprehensive guide is beginner-friendly but designed specifically with TypeScript developers in mind. If you’d like to code along or implement the examples provided, having prior TypeScript knowledge — though not necessarily advanced — is recommended. However, if you’re here to read and understand the concepts, you don’t need any prior programming experience to gain valuable insights.

Get Developer Support on Telegram: Click Here

What is SpaceComputer?

SpaceComputer is a space-based digital sovereignty layer built as a cryptographically secure compute protocol for applications across the blockchain stack.

Each node in the network is a Space Trusted Execution Environment (SpaceTEE). The hardware infrastructure layer is based on satellites equipped with “crypto engines” and data modules, serving as the decentralized physical infrastructure network (DePIN) in orbit.

SpaceComputer architecture features a two-layer system: the Celestial Chain in space, serving as the ultimate authority for immutable data history, and the Uncelestial layer on Earth for fast transaction pre-finality. This brings high security guarantees based on tamper-resistant hardware orbiting in the isolated space environment, ensuring resilience even during terrestrial network outages.

SpaceComputer was founded by a team of experienced software and hardware researchers, entrepreneurs, and developers with expertise in both space and blockchain technology. The project aims to enable a space-native economy, envisioning a future where commerce and computation extend beyond Earth, supported by its innovative satellite-powered blockchain network.

Understanding Orbitport and cTRNG

Orbitport is the gateway to celestial compute services built on the SpaceComputer infrastructure. Leveraging SpaceComputer nodes, Orbitport enables decentralized systems to consume data from tamper-proof hardware on satellites that run in low Earth orbit. Orbitport opens the possibilities of orbital computing services that transcend the limitations of terrestrial infrastructure. Its key offerings include:

  • cTRNG (cosmic True Random Number Generator): Employs data from cosmic radiation, detected via satellite instrumentation, to produce unbiased, cryptographically secure random numbers.
  • spaceTEE: Provides a secure and isolated environment designed specifically for the execution of sensitive, off-chain computational tasks, ensuring privacy and security.

Applications benefiting significantly from cTRNG’s unmatched randomness include online lotteries, gaming platforms, cryptographic key generation systems, blockchain-based smart contracts, and secure communication networks.

Technical Implementation

Authentication Flow

(Get your API access key here: https://spacecomputer.deform.cc/ctrngearlyaccess)

Orbitport ensures secure API access by using Auth0 authentication mechanisms. Below is a clear example of a server-side authentication implementation using TypeScript:

export const authenticateWithOrbitport = async () => {
  const clientId = process.env.ORBITPORT_CLIENT_ID;
  const clientSecret = process.env.ORBITPORT_CLIENT_SECRET;
  const authUrl = process.env.ORBITPORT_AUTH_URL;

  if (!clientId || !clientSecret || !authUrl) {
    throw new Error("Authentication configuration missing.");
  }

  const response = await fetch(`${authUrl}/oauth/token`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      client_id: clientId,
      client_secret: clientSecret,
      audience: "https://op.spacecomputer.io/api",
      grant_type: "client_credentials",
    }),
  });

  if (!response.ok) {
    throw new Error("Failed to retrieve access token.");
  }

  const data = await response.json();
  return data;
};

Design Rationale: Implementing authentication server-side minimizes exposure of credentials, significantly improving security and reducing potential vulnerabilities from client-side storage. It is advisable to encrypt and send the access tokens as an HTTP-only cookie from the server, rather than being stored or managed on the client side. We will visit this approach later on in the article

Random Seed Generation

The API endpoint below retrieves random seeds from Orbitport:

export const fetchRandomSeed = async (token: string) => {
  const apiUrl = process.env.ORBITPORT_API_URL;

  if (!apiUrl) {
    throw new Error("API URL configuration missing.");
  }

  const response = await fetch(`${apiUrl}/api/v1/rand_seed`, {
    method: "GET",
    headers: { Authorization: `Bearer ${token}` },
  });

  if (!response.ok) {
    throw new Error("Failed to retrieve random seed.");
  }

  const data = await response.json();
  return data;
};

Design Rationale: Routing requests through a server-side proxy centralizes error handling and maintains tighter control over API interactions, thus enhancing application security and reliability.

Client-Side Integration

Assuming you’re using a framework like React or NextJS, we can utilize React hooks on the client side to streamline interaction with Orbitport APIs:

import { useState } from "react";
import { useStore } from "@/store";

export const useOrbitport = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const getRandomSeed = async (token: string) => {
    setIsLoading(true);
    setError(null);

    try {
      const { access_token: token } = await authenticateWithOrbitport();
      const res = await fetchRandomSeed(token);

      if (!res.ok) throw new Error("Failed to retrieve random seed.");
      return await res.json();
    } catch (err) {
      setError(err as Error);
      throw err;
    } finally {
      setIsLoading(false);
    }
  };

  return { getRandomSeed, isLoading, error };
};

Design Rationale: Creating custom hooks streamlines repetitive API interactions, promotes cleaner code organization, and enhances reusability.

Practical Example: Random Planet Generation

Below is a simple example demonstrating the practical use of a random seed to select a planet:

const selectRandomPlanet = async () => {
  const seedData = await getRandomSeed();
  const planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"];

  const seedInt = parseInt(seedData.value.slice(0, 8), 16);
  const planet = planets[seedInt % planets.length];
  return planet;
};

This straightforward example clearly demonstrates random seed application without unnecessary complexity, facilitating easy integration into various projects.

Building A Cosmic Themed Lottery Game

With our foundational knowledge established, let’s build a simple web application showcasing the power of cTRNG. Note that this demonstration won’t extensively detail frontend development since it’s not the primary focus. The complete source code is available in the GitHub repository. This project utilizes NextJS.

We’ll create a Cosmic Wayfinder app, where users can explore space by discovering randomly generated planets, each with a unique name, image, and rarity. Clone the demo branch to get started:

git clone -b demo git@github.com:spacecomputerio/spacecomputer-orbitport-demo.git

All frontend components are already implemented. We will focus specifically on integrating API calls, token expiry management, hooks, and randomness logic.

Overview of Authentication & Random Seed Flow


(Get your API access key here: https://spacecomputer.deform.cc/ctrngearlyaccess)

Before we dive deeper into the technical implementation, let’s look at an overview of the approach that we are taking. Below is a swimlane diagram that illustrates the complete flow of authenticating users, managing token expiry, and retrieving random seeds from the Orbitport API.

Implementing Authentication and Managing Token Expiry

Instead of a dedicated authentication API route, we manage authentication through a centralized utility in lib/auth.ts. This approach simplifies managing token generation, storage, and expiry. Tokens are encrypted and stored in cookies, refreshing proactively based on a predefined expiry buffer (TOKEN_EXPIRE_BUFFER). That way, the actual access tokens are never exposed to the end users.

Token Handling Utility Functions:

  • generateAccessToken: Requests a new token from Orbitport.
  • getValidToken: Retrieves a valid token from cookies or generates a new one if expired or near expiry.

This centralized management ensures secure handling of tokens, minimizing unnecessary API calls by refreshing tokens efficiently.

// src/lib/auth.ts
async function generateAccessToken(): Promise<string | null> {
  const clientId = process.env.ORBITPORT_CLIENT_ID;
  const clientSecret = process.env.ORBITPORT_CLIENT_SECRET;
  const authUrl = process.env.ORBITPORT_AUTH_URL;

  if (!clientId || !clientSecret || !authUrl) {
    throw new Error("Missing Orbitport authentication configuration");
  }

  try {
    const response = await fetch(`${authUrl}/oauth/token`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        client_id: clientId,
        client_secret: clientSecret,
        audience: "https://op.spacecomputer.io/api",
        grant_type: "client_credentials",
      }),
    });

    if (!response.ok) {
      throw new Error("Failed to get access token");
    }

    const data = await response.json();
    return data.access_token;
  } catch (error) {
    console.error("Error getting access token:", error);
    return null;
  }
}

export async function getValidToken(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<string | null> {
  try {
    const encryptedToken = getEncryptedTokenFromCookies(req);
    let accessToken: string | null = null;

    if (encryptedToken) {
      const decrypted = decrypt(encryptedToken);
      const parsed = parseToken(decrypted);
      if (parsed) {
        const now = Math.floor(Date.now() / 1000);
        // Only use token if not expired and not about to expire
        if (parsed.exp > now + TOKEN_EXPIRE_BUFFER) {
          accessToken = parsed.access_token;
        } else {
          console.log("Token expired or about to expire");
        }
      }
    }

    // If no valid token, generate a new one
    if (!accessToken) {
      accessToken = await generateAccessToken();
      if (!accessToken) {
        console.error("Failed to get new access token");
        return null;
      }
      const parsed = parseToken(accessToken);
      if (!parsed) {
        console.error("Failed to parse new token");
        return null;
      }
      setEncryptedTokenCookie(res, accessToken, parsed.exp);
    }

    return accessToken;
  } catch (error) {
    console.error("Error in getValidToken:", error);
    return null;
  }
}

Fetching Random Seeds

Next, we will implement the user facing API route. With our authentication and token expiry management already set up, we can now implement the random seed fetching logic. This step is straightforward because we’ve done the heavy lifting previously. By simply using our existing getValidToken utility, we securely handle token verification and expiry management transparently. We can then use that to get a random seed by making the API call to Orbitport with the decrypted access token. We need to implement this in src/pages/api/random.ts:

// src/pages/api/random.ts
import { NextApiRequest, NextApiResponse } from "next";
import { getValidToken } from "@/lib/auth";

const ORBITPORT_API_URL = process.env.ORBITPORT_API_URL;

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    if (req.method !== "GET") {
      return res.status(405).json({ message: "Method not allowed" });
    }

    if (!process.env.ORBITPORT_API_URL) {
      return res.status(500).json({ message: "Missing Orbitport API URL" });
    }

    // Get valid token using our auth utility
    const accessToken = await getValidToken(req, res);
    if (!accessToken) {
      return res.status(401).json({ message: "Authentication failed" });
    }

    // Call downstream API with access token
    const response = await fetch(`${ORBITPORT_API_URL}/api/v1/rand_seed`, {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });

    if (!response.ok) {
      const errorText = await response.text();
      console.error("Orbitport API error:", errorText);
      return res
        .status(502)
        .json({ message: "Failed to get random seed", error: errorText });
    }

    const data = await response.json();
    return res.status(200).json(data);
  } catch (err) {
    console.error("Error in random endpoint:", err);
    return res.status(500).json({
      message: "Failed to get random seed",
      error: err instanceof Error ? err.message : "Unknown error",
    });
  }
}

Creating a Custom Hook

Finally, wrap up by creating a custom React hook for simplicity and reusability when fetching random seeds. This is implemented in src/hooks/useOrbitport.ts:

// src/hooks/useOrbitport.ts
import { useCallback } from "react";

interface RandomSeedResponse {
  value: string;
  sig: string;
  src: string;
}

export function useOrbitport() {
  const getRandomSeed = useCallback(async (): Promise<RandomSeedResponse> => {
    try {
      const response = await fetch("/api/random");
      if (!response.ok) {
        throw new Error("Failed to get random seed");
      }

      const data = await response.json();
      return data;
    } catch (error) {
      console.error("Error getting random seed:", error);
      throw error;
    }
  }, []);

  return {
    getRandomSeed,
  };
}

Now, just run npm run dev, navigate to localhost:3000, and your Cosmic Wayfinder is ready to launch!

Conclusion

SpaceComputer Orbitport offers a secure, reliable, and genuinely random solution powered by satellite technology. Its inherent robustness and tamper-resistant compute make it indispensable for applications requiring high levels of fairness, security, and resilience.

Resources

This article is by SpaceComputer DevRel Eason Chai. Follow him on X.