Gora Decentralized Oracle Documentation

Introduction

Gora enables blockchain programs (smart contracts) to interact with the outside world. Getting financial information from high-quality providers, extracting arbitrary data from public pages, calling online APIs or running Web Assembly code off-chain - is all made possible by Gora. To maintain security and trust, Gora relies on decentralization. A network of independent Gora nodes executes requested operations in parallel and certifies the outcome via reliable consensus procedure.

Gora structure and workflow overview diagram
Gora general structure and workflow

Customers interact with Gora in one of three roles: investor, node operator or smart contract developer. An investor purchases Gora tokens and delegates them to a node operator, receiving a share of rewards accrued from processing Gora requests. A node operator stakes Gora tokens (their own or investor's) and runs Gora Node Runner software to process Gora requests and receive rewards in Gora tokens. A smart contract developer writes applications that make use of Gora smart contract API's.

This document is aimed mainly at developers or node operators. Software engineers working with Gora-enabled blockchains, as well as companies interested in adding an oracle to blockchains they manage, will find technical information here. For Gora node operators, instructions and background information are also provided. Most recent and relevant products are described first, followed by info on legacy offerings.

App-specific Oracles (ASO's)

App-specific oracles (ASO's) are Gora's response to recent market demands. These are oracles designed to serve a certain web3 application or application type. While a general-purpose oracle strives for maximum flexibility to support all kinds or applications, it may lack specialized data processing or access control features needed for more niche use cases. For example, a sports oracle may want to provide team statistics which requires getting data from several resources and performing floating-point maths on it. A private oracle may want to only serve specific smart contracts or authenticate itself to data sources in a bespoke way.

Feature General-purpose oracle App-specific oracle
Ease of use Moderate High
Customizability Low High
Restrictions on data sources Not supported Up to ASO owner
Restrictions on users Not supported Up to ASO owner
Third-party monetization Not supported Via ASO owner's fee
Failover capability None Via multiple executors

Gora provides accesible and flexible tools to create your own ASO's and deploy them to EVM-compatible blockchains of your choice: either public, like Base or Polygon, or private, which organizations can run internally. Gora does this by combining its tried and true general-purpose oracle architecture with a powerful off-chain computation engine. Rather than simply fetching data online and passing it on for verification, Gora ASO requests execute oracle programs: tiny pieces of software that implement customizations and extensions to oracle functionality. Oracle programs can be easily created by customers deploying ASO's using programming languages of their choice.

ASO architecture and workflow

Gora ASO architecture and workflow diagram
Gora ASO architecture and workflow diagram

Gora's app-specific oracles rely on two key mechanisms: an ASO smart contract and an executor oracle. ASO smart contract contains oracle program and custom configuration required by customer for their specific use case. An executor oracle is a generic and complete oracle engine that implements fundamental oracle functionality using a distributed node network and consensus verification. They work together to serve web3 application requests as follows:

  1. An application smart contract makes a request for an oracle value by calling the ASO smart contract. It provides request parameters (if any) and expects a call back with a response.
  2. ASO smart contract combines received parameters with its configuration settings, its oracle program and makes a request to the executor oracle.
  3. Request to the executor oracle is picked up by decentralized network of nodes. Each online node runs the oracle program provided by the ASO smart contract. The program queries online data sources, processes received data and performs other programmed operations as needed to produce an oracle value.
  4. The produced value is submitted by each node to the executor smart contract for a proof-of-stake consensus verification. Upon reaching the configured threshold, the executor contract calls back ASO smart contract with the response. The ASO smart contract forwards the response to the application smart contract.

Gora provides common shared executors on a number of popular public blockchain networks. ASO customers just starting out are advised to use these. When data privacy, extra computing power or control over staking tokenomics is desired, customers are welcome to setup their own executors using Gora software. ASO smart contract can switch executors at any time.

Creating and managing ASO's

Gora application-specific oracles are normally created and updated using Gora's web-based ASO control panel. It provides a user-friendly way to handle all aspects of ASO's, including compiling oracle programs and testing them.

Gora web-based control panel in ASO architecture
Gora web-based control panel in ASO architecture

To start using Gora ASO control panel, go to https://aso.gora.io/ and connect your Web3 wallet by clicking "Connect Wallet". If you already created ASO's using the account selected in your wallet, you will be able to choose one from the drop-down list. You will also see a "Create new" button clicking which will make a new ASO for you.

Once you create a new ASO by clicking "Create new" button or select an existing one in the dropdown list, you will be presented with the ASO control panel. It contains properties of the currenty selected ASO for you to edit.

Control panel entry fields and their meanings are as follows:

ASO contract
Address of the ASO contract being configured
Description
Short string describing the ASO, e.g. "Footbal player rating"
Own fee
Amount in blockchain native currency that must be paid by the calling smart contract to make a request to the ASO, e.g. 0.0012.
Executor
Address of the executor oracle smart contract for the ASO to use. This should default to Gora shared executor address on this blockchain. A customer using their own custom executor will need to enter its address here.
Maxium executor fee
Highest amount that the ASO is allowed to pay for an executor oracle request. Executor request price is defined by the executor and can be fixed or varying to accomodate for market volatility. Empty field means no limit. Setting maximum executor fee allows to prevent ASO losing money: if the executor fee goes higher, ASO will decline requests. Every executor will also set the asset in which it will be paid - an ERC20 token or native currency. This asset will be auto-detected by the ASO, so it does not need to be configured here.
Source code
Source code for the oracle program - piece of software that queries data sources and produces an oracle value. This field is for for customer reference only and is not used by ASO. It may be best to leave it empty on mainnet blockchain networks for privacy and cost saving. Its primary purpose is convenience while developing on public testnets or local blockchain networks.
Compiled binary
Oracle program in compiled (binary form). This field can be populated compiling source code in the above field by pressing "Compile" button. Alternatively, users can upload their Web Assembly binaries converted to a hex string with optional gzip compression.

Oracle programs

An ASO oracle program is a compact piece of software that queries online data sources and produces an oracle value. Any ASO must have an oracle program to function, and usually it is written specifically for this ASO. While Gora ASO programs can be written in any language that compiles to Web Assembly, the ASO control panel and documentation examples use C language. It is simple, ubiquitous and can create very compact executables suitable for storage on the blockchain. To learn about oracle program API and control flow, please see Gora off-chain computation API.

Entering and compiling

No software installation is required to work with oracle programs. They can be written, compiled, tested and deployed inside ASO web control panel. To get started, click "Insert example" button in the control panel for a newly created ASO. The field (which must be empty) will be filled with a basic C program that returns string "Hello Gora!" as the oracle value. Clicking "Compile" button will compile this program and populate the compiled binary field.

Testing

ASO contol panel allows to test oracle programs before they are deployed to the blockchain. Pressing "Test locally" button will trigger compilation (when source code is present) and execution of the current oracle program. Click it to run the test and check out the result placed in the "Log messages" box. For programs that take arguments, they can be provided "Program arguments (JSON)" field as a JSON-formatted array. In a production environment, these arguments would come from args parameter of the request() method call to ASO smart contract.

Using ASO's

Gora app-specific oracles work using a simple callback pattern. To make an oracle request, customer smart contract calls ASO smart contract's request method. If parameters need to be passed to the oracle program, they are supplied as the method argument (array of byte strings). Unique request ID is returned by ASO for future reference. On successful request completion, customer smart contract gets a response call to its special __goraAsoResponse method from the same ASO smart contract. The call has two arguments: request ID to match the response to the initiated request, and the actual value returned by the oracle.

To get a feel of it, consider the following contrived Solidity fragment that might occur in a smart contract tracking Bitcoin price and DowJones Industrial Average index:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// ASO smart contracts to query, addresses will be known and chain-specific.
GoraAso rateAso(0xaaaaaaaaaaaaaaaaaaaaa);
GoraAso dowJonesAso(0xbbbbbbbbbbbbbbbbbbbbb);

// Local storage to track requests in flight.
enum RequestType { None, BitcoinPrice, DowJones };
mapping(bytes32 => RequestType) requests;

// Values to keep up to date. Byte strings for simplicity, but in
// real-world apps these are usually unpacked into more suitable formats.
bytes bitcoinPrice;
bytes dowJones;

// Request a Bitcoin price update.
function requestBitcoinPrice() external {
  bytes[] memory reqParams = new bytes[](2);
  reqParams[0] = bytes("btc");
  reqParams[1] = bytes("usd");
  bytes32 reqId = rateAso.request(reqParams);
  requests[reqId] = RequestType.BitcoinPrice;
}

// Request a Dow Jones index update.
function requestDowJones() external {
  bytes32 reqId = dowJonesAso.request(new bytes[]());
  requests[reqId] = RequestType.DowJones;
}

// Handle oracle responses.
function __goraAsoResponse(bytes32 reqId, bytes calldata value) external {
  if (requests[reqId] == RequestType.BitcoinPrice)
    bitcoinPrice = value;
  else if (requests[reqId] == RequestType.DowJones)
    dowJones = value;
  else
    revert("Response to an unknown request");
  delete requests[reqId];
}

For complete working examples demonstrating uses of Gora ASO, please see the examples repository.

Executor oracles

Every ASO relies on an executor oracle (executor) for basic lower-level blockchain oracle operations. Separating ASO's and executors allows for more flexibility, failover capabilities and a seamless customer upgrade path from shared to private infrastructure. Gora recommends new ASO customers to start with a shared executor.

Features Shared executor Custom executor
Managed by Gora ASO owner
Requires setup and configuration No Yes
Private data sources Not supported Configurable
Node software customization Not supported Possible
Node hardware capabilities Limited Up to ASO owner
Payment options GORA token Any ERC20 token

Shared executors

Gora provides shared executors for ASO customer use. These are essentially generic oracles relying on a decentralized network of nodes for data querying and validation. Node operators use Gora tokens to make stakes for proof-of-stake valudation and to receive rewards for fulfilling oracle requests. Customers using a shared Gora executor must therefore fund their ASO smart contract with Gora tokens and maintain their balance as they are being spent.

To use a Gora shared executor, set your ASO executor address according to network being used:

Blockchain Network Address Fee asset Fee amount
Base Sepolia 0x627e0C53aF5Bb97610A8146F931188FCB43C9B49 GORA 1
Base Mainnet 0xd4c99F88095F32dF993030d9a6080e3BE723F617 GORA 1
Polygon Testnet TBA GORA 1
Polygon Mainnet TBA GORA 1

When using a testnet, visit Gora testnet faucet to get tokens for funding your ASO contract.

Custom executors

Shared executors rely on distributed networks of nodes run by general public. This may not be suitable for certain use cases. For example, when private data (such as keys) is used for querying data sources, or when oracle programs use exceptionally large amounts of computing resources.

For these kinds of situations, Gora provides a way for customers to deploy their own executors. Once customer deploys an executor smart contract, they can bring up a separate node network under their own management. Standard Gora software which can work with private authentication keys can be used to run it, or Gora can customize its node software for customer's specific needs.

At this time, creating custom executors is a semi-manual process, with a completely automated tool being on the roadmap. If you would like to explore this option, please contact Gora.

Classic oracle on EVM

Classic oracle is the original Gora product designed to query any type of data source. On EVM-compatible networks, smart contracts are almost always written in Solidity, so this is the language Gora uses. For a quick hands-on introduction, see Developer Quick Start (EVM). For a more complete overview as well as an API reference, read on.

Requesting oracle data

Gora functionality is accessed by calling methods of Gora main smart contract. To get started, you need Gora main contract address for the blockchain network that you are going to use. The preferred way to find it is to check the home page of Gora Explorer for the network in question. For example, Gora Explorer for Base mainnet. Gora main contract address is shown next to "Gora App" label.

With Gora main contract address, you can create a Gora API Solidity object in your smart contract and start making Gora calls. For example, read total amount of tokens currently staked in this Gora network:

1
2
3
address constant goraMainAddr = address(0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
Gora gora = Gora(goraMainAddr);
uint totalStake = gora.totalStake();

Oracle data is requested from Gora by calling request method of Gora main smart contract. In its simplest form, it takes just two positional arguments:

Argument # ABI Type Description
0 string Data source specification
1 bytes Data source parameter

For example:

1
bytes32 reqId = gora.request("http://example.com/mydata", bytes("substr:0,2"))

More precisely, Gora request method arguments have the following meanings:

Data source specification
Specifies the data source and method to access it. It has the structure of a standard URL, e.g. http://some-source.example.com/some_data.json. Besides HTTP(S), request URLs may use Gora-specific access protocols. For example, gora://classic/1 specifies test source that always returns 1, without querying external endpoints.
Data source parameter
For sources that are not special (i.e. do not begin with gora://) this parameter contains a value extraction specification. It describes how oracle-returned value is to be extracted from data provided by the source. For example, with a JSON endpoint that returns { "score": 123 } one would specify: jsonpath:$.score. Gora supports a number of value extraction options which will be explained in detail below. Special Gora sources will be described separately.

Return value of the request method is a unique identifier for the created request. It is necessary to map returned oracle values to requests when making multiple oracle calls, to manipulate created requests or to access their properties.

Receiving oracle data

After your Gora request is created and committed to public blockchain, it should be picked up and processed by Gora nodes in short order. Data extracted by nodes according to your specifications will be put through consensus by Gora smart contracts. On successful verification, Gora main smart contract will call the method you specified in your request and provide the resulting value. Your data-receiving method must only accept two arguments:

Argument # ABI Type Description
0 bytes32 Request ID
1 bytes Oracle value

Namely:

Request ID
Identifier of Gora request for which the value provided is the response. You smart contract will likely want to use it to determine which of the Gora requests made previously this response applies to.
Oracle value
Value returned by the oracle, as a byte string. Numeric values will be provided as their string representaitons, e.g. "0.1234", "-12". It will be down to receiving smart contract to convert them to Solidity numeric types as needed. Strings are returned as is.

Using off-chain computation

For use cases that require more flexibility, Gora supports oracle requests that execute user-supplied Web Assembly to produce an oracle value. This enables querying data sources determined at runtime as well as processing queried data in arbitrary ways. User-supplied code is executed off-chain by Gora nodes and is subject to resource limits.

Gora off-chain computation workflow diagram
Gora off-chain computation workflow

To make use of this feature, developers write their off-chain programs utilizing Gora off-chain API. They may use any language that compiles to Web Assembly. We recommend C language due to its simplicity and ubiquity, and Clang compiler because it generates Web Assembly binaries directly. E.g.:

$ clang example.c -Os --target=wasm32-unknown-unknown-wasm -c -o example.wasm

Compiled binary is then encoded as Base64Url (URL-safe variant of Base64) and included with the request to a special URL defined by Gora to handle off-chain computation requests. In simpler form, where web assembly executable binary is provided in smart contract source code, this URL has the following format: gora://offchain/v<API version>/basic?body=<Base64Url-encoded WASM binary>[optional positional arguments].

The executable body can also be supplied in binary form as the data source parameter which is often convenient with larger executables or automated builds. In that case, the body data source URL parameter is omitted. Current Gora offchain API version is 0. So, for example, to execute a program with two positional arguments ("red" and "apple") one would specify the following URL: gora://offchain/v0/basic?arg=red&arg=apple&body=AGFzbQEAAAABhoCAg...

To convert binaries into Base64URL encoding, basenc command-line utility, normally included with Linux and MacOs, can be used:

$ basenc --base64url example.wasm
AGFzbQEAAAABhoCAgAABYAF/AX8CuoCAgAACA2Vudg9fX2xpbmVhcl9tZW1vcnkCAAEDZW52GV9f
aW5kaXJlY3RfZnVuY3Rpb25fdGFibGUBcAAAA4KAgIAAAQAHjICAgAABCGdvcmFNYWluAAAMgYCA
gAABCpGAgIAAAQ8AIABBgICAgAA2AghBAAsLk4CAgAABAEEACw1IZWxsbyB3b3JsZCEAAMKAgIAA
B2xpbmtpbmcCCJuAgIAAAgCkAQAJZ29yYV9tYWluAQIGLkwuc3RyAAANBZKAgIAAAQ4ucm9kYXRh
Li5MLnN0cgABAJGAgIAACnJlbG9jLkNPREUFAQQGAQAApoCAgAAJcHJvZHVjZXJzAQxwcm9jZXNz
ZWQtYnkBBWNsYW5nBjE2LjAuNgCsgICAAA90YXJnZXRfZmVhdHVyZXMCKw9tdXRhYmxlLWdsb2Jh
bHMrCHNpZ24tZXh0
$

Gzip compression can be applied before encoding to reduce blockchain storage use:

gzip < example.wasm | basenc --base64url

Gora will automatically recognize and decompress gzipped Web Assembly binaries.

Gora off-chain computation API

Oracle programs interact with the host node via Gora off-Chain API. It is essentially a customized Web Assembly environment that provides functionality to query data sources, fetch results, write log messages and more. A key part of this API is support for repeated program execution in the context of the same oracle request. This is necessary because Web Assembly programs cannot efficiently pause while waiting for asynchronous operations, such as receiving data from online sources.

Off-chain programs in fulfilling oracle requests
Off-chain programs in fulfilling oracle requests

Gora off-chain API is made available to C programs by including gora_off_chain.h header file. When compiling via ASO control panel, it is made available for inclusion automatically. It defines the following custom functions:

void gora_request_url(const char* url, const char* value_specs)
Request content from an URL. value_specs argument contains one or more value extraction specifications, separated by tab characters.
void gora_set_next_url_param(const char* value)
Set value of a template parameter in the URL most recently requested with gora_request_url(). For example, after calling gora_request_url("https://example.com/?a=##&b=##"), one can call gora_set_next_url_param("one"), then gora_set_next_url_param("two") which would result in URL "https://example.com/?a=one&b=two" being requested. This allows having predefined templates for data source URLs and filling them at runtime.
void gora_log(const char* message, const int level)
Write a message to the node log. Intended for debugging only, oracle program logging is disabled by default on production nodes.

In addition to functions, Gora off-Chain API defines a context data structure. It is designed for passing data from host node to oracle program as well as preserving current state between execution stages (more on that later). An instance of this structure is passed to oracle program whenever it executes. It contains:

  • API version information for compatibility checks
  • Arguments passed to the program with the oracle request
  • Values from queried data sources extracted by host for the program
  • Oracle value to be returned, set by the program
  • Current execution stage number
  • Scratch memory for program data to persist between execution stages

Complete definition of the context structure is contained in gora_off_chain.h header file which all oracle program developers are advised to peruse.

Staged execution

Execution of oracle programs in stages is necessary because, like most low-level system languages, Web Assembly does not support asynchronous calls. When a Web Assembly program needs to retrieve data from a source that cannot return it instantly (e.g. a network endpoint), it has to either constantly check for data arrival in a loop (very inefficient) or rely on runtime environment to call it when the data is ready. Gora off-chain API implements a variant of the second approach.

Gora host node executes the program repeatedly, performing asynchronous operations between executions which are called stages. A stage starts when program's main function is called by the host node and ends when this function returns. During a stage, the program can schedule HTTP(S) requests, possibly using URL templates that it can fill at run time. When a stage ends, these requests are executed by the host node. On their completion, next stage commences.

Request results are made available to the program via the context structure. The context contains current stage number, so program always knows which stage it is at. It also has persistent memory space to share data between stages. Finishing a stage, the program's main function returns a value telling the host node what to do next: execute the next stage, finish successfully or terminate with a specific error code. For a hands-on primer of using staged execution, please see example programs.

Developer Quick Start (EVM)

Developer Quick Start (EVM) is a package of code examples and scripts to help developers start using Gora from their EVM blockchain applications. It contains instructions for Gora local development environment, example applications also usable as templates and Solidity compiler and EVM node Linux binaries.

Solidity examples

The following extensively commented examples are provided as hands-on documentation and potential templates for your own applications:

  • example_basic.sol - makes the simplest possible type of query, fetching a predefined value from a Gora built-in test data source.
  • example_off_chain.sol - demonstrates Gora off-chain computation capability by making two runtime-defined JSON API requests to fetch air temperature at a British postcode.

Setting up local development environment

Following the steps below will set you up with a complete environment for compiling and deploying Gora smart contract examples.

  1. Check operating system compatibility

    Open a terminal session and execute: uname. If this prints out Linux, continue to the next step. If the output is anything else, you may proceed at your own risk, but with a non-Unix OS you will almost certainly fail.

  2. Clone this repository

    Install Git if not already done so, then run:

    git clone https://github.com/GoraNetwork/developer-quick-start

    You should get an output like:

    Cloning into 'developer-quick-start'...
    remote: Enumerating objects: 790, done.
    remote: Counting objects: 100% (232/232), done.
    remote: Compressing objects: 100% (145/145), done.
    remote: Total 790 (delta 156), reused 159 (delta 85), pack-reused 558 (from 1)
    Receiving objects: 100% (790/790), 67.78 MiB | 1.43 MiB/s, done.
    Resolving deltas: 100% (469/469), done.
  3. Change to EVM subdirectory and install NPM dependencies:
    cd developer-quick-start/evm
    npm i

    You should then see something like this:

    added 9 packages, and audited 10 packages in 3s
    3 packages are looking for funding
      run npm fund for details
    found 0 vulnerabilities

    If no errors popped up, proceed to the next step.

  4. Setup target blockchain network
    Option A: Use local development blockchain network
    Run ./start_dev_env. The script will start up, displaying log output from local EVM nodes as well as local Gora node. It must be running while you deploy and run the example scripts. To terminate the script, ending your development session, hit, Ctrl-C.
    Option B: Use a public network

    Public network configuration is set via environment variables. For example, to use Base Sepolia you would execute:

    export GORA_EXAMPLE_EVM_MAIN_ADDR=0xcb201275cb25a589f3877912815d5f17f66d4f13
    export GORA_EXAMPLE_EVM_API_URL=https://sepolia.base.org
    export GORA_EXAMPLE_EVM_KEY=./my_base_sepolia_private_hex_key.txt

    ./my_base_sepolia_private_hex_key.txt is the example path to a text file containing private key for the account you want to use for deployment, in hex form. It can usually be found in account tools section of wallet software such as Metamask. The environment variables will be picked up by the example-running script discussed below. It should be possible to deploy example scripts to any public EVM network using this method. Deploying to a mainnets is, however, strongly discouraged for security reasons.

Running and modifying the examples

For local development environment (option A in step 4 above), open another terminal window and change to the same directory in which you started the setup script. For public network configurtion (option B in step 4), please remain in the same terminal session.

Then execute:

./run_example basic

or

./run_example off_chain

This should compile, deploy and run the example, providing detailed information on the outcome. For further details, consider Solidity examples. You are welcome to modify the examples source code and try it repeating the step above.

Composition of the development environment

Gora EVM local development environment relies on the following pieces of software:

  • Solidity compiler (solc binary). Used to compile examples and potentially developer's own code.
  • Geth EVM node software (geth binary). Provides local blockchain functionality to model master (L1) and slave (L2) EVM networks. Both instances of Geth are run in development mode (with --dev switch).
  • Gora smart contracts (files with .compiled extension), compiled into combined JSON format.

start_dev_env script starts Geth instance, deploys Gora smart contracts and stays in the foreground, displaying log messages from the above as they come. Contrary to Gora Developer Quick Start package for Algorand, it must be running at all times to run Gora smart contracts locally. There is no way to start a Gora node or its local blockchain on-demand on per-example basis. To end your development session and terminate the script, hit Ctrl-C in the terminal window running it.

Classic oracle on Algorand

Customer applications interact with Gora via smart contract calls. For a quick hands-on introduction to using Gora from your smart contracts, please see Gora Developer Quick Start GitHub repository. For a complete reference and instructions on calling Gora from JavaScript, read on.

Requesting oracle data

Gora requests are made by calling request method of the main Gora smart contract. This contract's application ID is a part of Gora network configuration and can be found using info command of the Gora CLI tool. For example (with irrelevant output removed):

$ ./gora info
...
Main app ID: 439550742
...
$

request method accepts the following arguments:

Name ABI Type Description
request_args byte[] Encoded request specification
destination byte[] Encoded destination call specificaiton
type uint64 Request type ID
key byte[] Unique request key
app_refs uint64[] App references to pass through to destination
asset_refs uint64[] Asset references to pass through to destination
account_refs address[] Account references to pass through to destination
box_refs (byte[],uint64)[] Box references to pass through to destination

Encoding request specification

A request specification is an instance of a structured Algorand ABI type. The exact ABI type depends on the type of oracle request being specified, but it is always supplied to the request method encoded as a byte string. This allows Gora to add new request types without changing its smart contracts.

Currently Gora supports three request types:

  • Type #1 - classic requests, querying sources predefined by Gora
  • Type #2 - general URL requests, for arbitrary URLs and advanced data extraction methods
  • Type #3 - off-chain computation requests

At the highest level of Algorand ABI type definition, all of these types have the following structure:

tuple(source_spec[], aggregation, user_data).

Where:

  • source_spec[] - array of source specifications. A single oracle request can query multiple sources.
  • aggregation: uint32 - how results from the sources are aggregated (0 - no aggregation, 1 - maximum, 2 - minimum, 3 - average).
  • user_data: byte[] - any user-supplied data to attach to request and its response

A a source specification is a structured Algorand ABI type instance that describes an oracle source query. Its exact ABI type depends on the request type and is described below.

Gora oracle request types diagram
Gora oracle request types

Request type #1 - classic requests

This is the original Gora request type which relies on oracle source definitions bundled with GNR. Source specifications for requests of this type contain the following fields:

Name ABI Type Description
source_id uint32 numeric id of an oracle source
source_args byte[][] positional arguments to the source
max_age uint32 maximum age of source data in seconds to be considered valid

Available sources and arguments applicable to them can be examined by running: gora dev-sources. The list of classic sources is defined by Gora is subject to extension in future releases.

Parametrized sources

To add flexibility to classic requests, certain source properties can be specified on per-query basis.

For example, if a source provides many pieces of data from the same endpoint, it is more convenient to let the requester specify the ones they want than to define a separate source for each. This is achieved by parametrizing the value_path property. Setting it to ##0 in the oracle source definition will make Gora nodes take its value from 0'th argument of the request being served. Parameter placeholders can just as well be placed inside strings where they will be substituted, e.g. http://example.com/##2&a=123.

The following oracle source definition properties can be parametrized: url, value_path, timestamp_path, value_type, value, round_to, gateway. Substituted values are always treated as strings. For example, when supplying a parameter to set round_to field to 5, the string "5" must be used rather than numeric value of 5.

Request type #2 - general URL requests

This type of oracle request does not depend on a pre-configured list of oracle sources and allows authentication via third party without compromising decentralization. Source specifications for requests of this type contain the following fields:

Name ABI Type Description
url byte[] source URL to query
auth_Url byte[] authenticator URL
value_expr byte[] expression to extract value from response
timestamp_expr byte[] expression to extract timestamp from response
max_age uint32 maximum age of data in seconds to be considered valid
value_type uint8 return value type: 0 for string, 1 for number
round_to uint8 number of digits to round result to (0 for no rounding)
gateway_url byte[] gateway url (not for general use)
reserved_0 byte[] reserved for future use
reserved_1 byte[] reserved for future use
reserved_2 uint32 reserved for future use
reserved_3 uint32 reserved for future use

Third-party authentication

General URL requests support using third party services to access sources that require authentication. For example, a price data feeds provider may protect their paid endpoints by requiring an access key (password) in URLs. Since everything stored by the blockchain is public, authentication keys cannot be held by smart contracts or included in oracle requests. Node operators may configure their own access keys for some sources, but not in the general case. Third-party authentication services that issue one-time authentication keys on per-request basis are designed to fill that gap. When auth_url field in the source specification is filled, Node Runner software will call this URL and receive a temporary auth key. The authenticator service will check that the node runner and the oracle request are both eligible to receive it.

Request type #3 - off-chain computation

For use cases that require even more flexibility, Gora supports oracle requests that execute user-supplied Web Assembly code. The code is executed off-chain by Gora network nodes and is subject to resource limits.

Gora off-chain computation workflow diagram
Gora off-chain computation workflow

To make use of this feature the developer must write their program using Gora Off-Chain API in any language that compiles to Web Assembly. Compiled binary is then made available to Gora network nodes in one of the three ways: verbatim as a request parameter (for small programs), in on-chain box storage or as a download at a public URL. Request specification ABI type for this kind of request has the following structure:

Name ABI Type Description
api_version uint32 minimum off-chain API version required
spec_type uint8 how executable is specified (see below)
exec_spec bytes[] executable specification
exec_args bytes[][] positional arguments to the executable
reserved_0 bytes[] reserved for future use
reserved_1 bytes[] reserved for future use
reserved_2 uint32 reserved for future use
reserved_3 uint32 reserved for future use

spec_type value determines what is contained in exec_spec as follows:

  • 0 - executable body itself
  • 1 - 8-byte app ID followed by box name for reading from on-chain box storage
  • 2 - URL to fetch the executable from

To get a grasp of Gora Off-Chain API and execution model, start with this example program: example_off_chain_basic.c. It returns the phrase "Hello world!" as an oracle value and is self-explanatory. To compile it, install Clang C compiler version 12 or newer and run:

clang example_off_chain_basic.c -Os --target=wasm32-unknown-unknown-wasm -c -o example_off_chain_basic.wasm

For a more advanced example, featuring URL requests and asynchronous operations, see: example_off_chain_multi_step.c.

This program does useful work and is extensively commented. It takes a British postcode as a parameter, queries two data sources, building their URLs dynamically, and returns current air temperature in the area of said postcode. This requires two data-retrival operations: getting postcode geographical coordinates and querying current weather at them.

Because of certain limitations of Web Assembly, programs cannot efficiently pause while waiting to receive data from extrnal sources such as URLs. To work around that, Gora off-chain programs are run in steps. Steps are essentially repeated executions of the program with a shared context that includes current execution number. A step starts when the program's main function is called by the executing node and ends when it returns.

During a step, the program can schedule HTTP(S) requests, possibly using URL templates that it can fill at run time. When the step ends, these requests are executed by the Gora node and on their completion, the next step commences. The program can access request results as well as other node-provided data such as the number of step currently executing via data structure passed to it as an argument.

Finishing a step, the program always returns a value which tells the Gora node what to do next: execute another step, finish successfully or terminate with a specific error code. For the list of valid return values, see gora_off_chain.h header file.

To compile this example program, run:

clang example_off_chain_multi_step.c -Os --target=wasm32-unknown-unknown-wasm -c -o example_off_chain_multi_step.wasm

To execute the compiled binary using Gora CLI and default test destination app, run:

gora request --off-chain ./off_chain_example.wasm --args sm14hp

Multi-value requests and responses

This feature allows requests of type 1 and 2 to fetch multiple pieces of data from the same source response. Normally, value_path property contains a single expression, so just one value is returned by an oracle request. To return multiple values, it is possible to specify multiple expressions separated by tab character. For example: $.date\t$.time\t$.details.name. Since an oracle return value must be a single byte string for the consensus to work, returned pieces of data are packed into Algorand ABI type - an array of strings:

1
const multiResponse = new Algosdk.ABIArrayDynamicType(Algosdk.ABIType.from("byte[]"));

To access individual results, smart contract handling the oracle response must unpack this ABI type. Nth string in the array will correspond to the nth expression in the valuePath field.

Rounding numeric response values

Certain kinds of data, such as cryptocurrency exchange rates, are so volatile that different Gora nodes are likely to get slightly different results despite querying them at almost the same time. To achieve consensus between nodes when using such sources, Gora can mathematically round queried values.

A source that supports rounding will have "Round to digits" field when shown with gora dev-sources command. Usually, the rounding setting will be parametrized, for example: "Round to digits: ##3". This means that the number of significant digits to round to is supplied in parameter with index 3. The number must be provided in string representation, like all parameters. Rounding will only affect the fractional part of the rounded number, all integer digits are always preserved. For example, if rounding parameter is set to "7", the number 123890.7251 will be rounded to 123890.7, but the number 98765430 will remain unaffected.

Calling from outside of blockchain

While Gora's main purpose is to interact with smart contracts, it is sometimes desirable to access its functionality from normal Linux software. Examples below will be given in JavaScript, but they can be adapted to any language supported by the Algorand API, such as Python or Go. We start by building the request spec ABI type to encode our request. It can be accomplished in a single call, but will be done in steps here for clarity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
 const Algosdk = require("algosdk");

 const basicTypes = {
   sourceArgList: new Algosdk.ABIArrayDynamicType(Algosdk.ABIType.from("byte[]")),
   sourceId: Algosdk.ABIType.from("uint32"),
   maxAge: Algosdk.ABIType.from("uint32"),
   userData: Algosdk.ABIType.from("byte[]"),
   aggregation: Algosdk.ABIType.from("uint32"),
 };

 const sourceSpecType = new Algosdk.ABITupleType([
   basicTypes.sourceId,
   basicTypes.sourceArgList,
   basicTypes.maxAge
 ]);

 const requestSpecType = new Algosdk.ABITupleType([
   new Algosdk.ABIArrayDynamicType(sourceSpecType),
   basicTypes.aggregation,
   basicTypes.userData
 ]);

Now we will use requestSpecType ABI type that we just created to encode a hypothetical Oracle request. We will query two sources for USD/EUR price pair and receive their average value. The data must be no more than an hour old in both cases. The sources are predefined in Gora with IDs 2 and 5, but one specifies currencies mnemonically while the other does it numerically:

1
2
3
4
5
6
7
8
const requestSpec = requestSpecType.encode([
  [
    [ 2, [ Buffer.from("usd"), Buffer.from("eur") ], 3600 ],
    [ 5, [ Buffer.from([ 12 ]), Buffer.from([ 44 ]) ], 3600 ],
  ],
  3, // average it
  Buffer.from("test") // let the receiving smart contract know it's a test
]);

The requestSpec variable can now be used for spec argument when calling the request method for Gora main smart contract.

Decoding request responses

Results of an oracle request are returned by calling dest_method method of the smart contract specified in dest_id. The method gets passed the following two arguments:

  • type: uint32 - response type; currently is always 1.
  • body: byte[] - encoded body of the response (details below).

The body argument contains an ABI-encoded tuple of the following structure:

  • byte[] - request ID. Currently the same as Algorand transaction ID of the request smart contract call that initiated the request.
  • address - address of the account making the request
  • byte[] - oracle return value, more details below
  • byte[] - data specified in userData field of the request
  • uint32 - result error code, see below
  • uint64 - bit field with bits corresponding to the request sources; if n'th bit is set, the n'th source has failed to yield a valid value.

Result error codes

  • 0 - normal result.
  • 1 - result was truncated because it was over the allowed size. Result size limit is configured in Node Runner software and depends on maximum smart contract arguments size supported by Algorand.

Unless the numeric type has been explicitly specified for the return value, it will be encoded as a string. If value expression is a JSON path that matches an object, it will stringified, e.g. '{ "date": "01-01-2020", "price": 123 }'.

Numeric oracle return values

When returned oracle value is a number, it is encoded into a 17-byte array. 0's byte encodes value type:

  • 0 - empty value (not-a-number, NaN)
  • 1 - positive number
  • 2 - negative number

Bytes 1 - 8 contain the integer part, 9 - 17 - the decimal fraction part, as big endian uint64's. For example, 0x021000000000000000ff00000000000000 in memory order (first byte has 0 offset) decodes as -16.255

Troubleshooting applications

Troubleshooting Gora applications begins with making oracle requests and looking at how they are handled in each processing phase. For that, we recommend using Gora CLI tool, a Gora observer node and Algorand Dapp Flow web app. The rest of this section will walk you through setting them up and using them to trace execution of a Gora request.

Observer node

Gora observer node is a node set up and running on a Gora network for the purpose of monitoring requests. An observer node is not required to run continuously or have any GORA tokens staked. When using Developer Quick Start, setting up an observer node is not necessary because it includes a full Gora node. For troubleshooting applications on Algorand testnet or mainnet, if you are not already running a normal Gora node on the same network, set on up following the Getting Started section above.

Checking that your application is making request calls

Now you can find out Algorand address of the application from which you are making Gora requests. This can be done with Algorand Dapp Flow Explorer: enter your application ID into the search box and press Enter which should take you to application transactions page. The address should be displayed under "Application account" label.

Make sure you have set up your observer node as its configuration is used by Gora CLI tool. Now run the tool to find out Gora main smart contract ID:

$ gora info

You should get output containing a string like:

Main app ID: 439550742

Now you can use Dapp Flow to check that oracle request calls are being made from your application to correct Gora smart contract. Try running your app, then search on Dapp Flow for transactions to Gora main app ID. There must be an application call transaction from your app address just made.

Monitoring how your requests are processed

Once your Gora request call gets stored on the blockchain, it is up for detection and processing by Gora nodes. That including your observer node, which you will now utilize to monitor processing of your requests. If you are not using Developer Quick Start, you will need to enable debug output on your node. Open your node config file (~/.gora by default) and under "deployment" section add the following lines:

"logLevel": 5

Make sure to add a comma to the previous line if there is one or you will get a config syntax error when trying to start the node. Restart the node if it is already running.

If your observer node hasn't been running, start it now and keep an eye on its log messages: either by running it in the foreground or by tailing logs with docker logs -f <node container name>.

Now when your Gora blockchain app makes another request, you should see your node pick up the request and log detailed messages on various phases of its processing. For example, with a General URL request:

2023-12-10T20:46:54.432Z DEBUG Handling call "main#1003.request" from "Z7PANAMW2I7MEHTTT24U2G5UJXUSIO6QORYCJV6YVZZQNBVQ2Z22C4P5XI", round "81754"
2023-12-10T20:46:54.441Z INFO  Processing oracle request "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA", destination: "1516.handle_oracle_url"
2023-12-10T20:46:54.441Z DEBUG Querying URL source: "https://coinmarketcap.com/currencies/bnb/, "regex:>BNB is (?:up|down) ([.0-9]+)% in the last 24 hours, "", ""
2023-12-10T20:46:54.507Z DEBUG Fetching "https://coinmarketcap.com/currencies/bnb/", time limit (ms): 5000, size limit (bytes): 1048576
2023-12-10T20:46:54.548Z DEBUG Querying URL source: "https://coinmarketcap.com/currencies/solana/, "regex:>Solana is (?:up|down) ([.0-9]+)% in the last 24 hours, "", ""
2023-12-10T20:46:54.627Z DEBUG Fetching "https://coinmarketcap.com/currencies/solana/", time limit (ms): 5000, size limit (bytes): 1048576
2023-12-10T20:46:54.865Z DEBUG Fetched "https://coinmarketcap.com/currencies/solana/", "315317" bytes, starting with: "<!DOCTYPE html><html"...
2023-12-10T20:46:54.886Z DEBUG Result #1, source "https://coinmarketcap.com/currencies/solana/": "6.41", for "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA"
2023-12-10T20:46:55.342Z DEBUG Decoding gzip
2023-12-10T20:46:55.360Z DEBUG Fetched "https://coinmarketcap.com/currencies/bnb/", "335244" bytes, starting with: "<!DOCTYPE html><html"...
2023-12-10T20:46:55.363Z DEBUG Result #0, source "https://coinmarketcap.com/currencies/bnb/": "0.53", for "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA"
2023-12-10T20:46:55.364Z DEBUG Result for "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA": 6.41 (number, aggregation: "2")
2023-12-10T20:46:55.377Z DEBUG Using seed: "0x1ea6cbe0dac0d99beb3903648fc155327c93c870c08106a9b66a7b271e7345d3"
2023-12-10T20:46:55.383Z DEBUG Alloted "1000004424" vote(s) for "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA", zIndex: "1"
2023-12-10T20:46:55.403Z DEBUG Creating verify txn to vote on "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA": { suggestedParams: { flatFee: true, fee: 0, firstRound: 81755, lastRound: 81764, genesisID: 'sandnet-v1', genesisHash: 'RXrzSgzbMh2FXnMJPwqL2UGeyIdbiks2G1oUvDS7fA8=', minFee: 1000 }, from: 'GBS6GNRJIOD3SFHQGCXT7QBUF2V6G7HHG7J3M3XYSAF57FIN4RN53DTRTU', appIndex: 1003, appArgs: [ '0x23fd2961', '0x8944db7ce5abc02130dcc5bb96ee1c8a7c3a1ee8022b0bfb81b28581764b4695f60dfcaf9ffe2193f538c0df2d43e7b4a9f85a0f4cc12e4dd5d2df8bb0d1f034', '0xd50e00ddaa15a2f5181e46c3910100df4c5808230eef87df14d56ea5a7d40b4a468c5c656f3ec347a5344dc267df2aab6fdc92d711649fe692804c1614b98e47112b67866010c6ac1de6bcf26a51f609', '0x1ea6cbe0dac0d99beb3903648fc155327c93c870c08106a9b66a7b271e7345d3', '0x0000000000000001', '0x0000000000000002', '0x0000000000000003', '0x0000000000000001', '0xb3cf668b6f5b53016300c0f95dbd981ef336588d3753ae4bf77b29132afefb78', '0x55e47eeb0b4579748653a796eace4ac2b87a836e30375e2b1a1bdcc81dce86bf978a7fa15bc7d7446919fe923abdb361de0bdf61252fd8db49e805e0f17ec563000000003b9ab8b8000000e8d4a51000000000000000000a0000000000000000d9fd2c74d7ff4f2eaf66d681a0f53f9368213eac7b75719ad7aa2e96461d2a5a80', '0x0000000000000004' ], accounts: [ 'YHZYUAYUIYNXFMLK5WZ7PYGHVQUIEYULHAKGF5MCYSG76OYP2TYT2WQZRM', '3ACWF4HKPTGU555RKFF6KETS56EOEBO4OSL4BTS46XDHHIHPTNOBY4TRSU', 'TRWQJHM24P64L2XY35IFCQ4DXGMBBVKB5VP6IVDRSQYN22R2VTBHTR7JB4', '3H6SY5GX75HS5L3G22A2B5J7SNUCCPVMPN2XDGWXVIXJMRQ5FJNAF6XE4Y' ], foreignApps: [ 1009 ], boxes: [ { appIndex: 1003, name: '0xb3cf668b6f5b53016300c0f95dbd981ef336588d3753ae4bf77b29132afefb78' }, { appIndex: 1003, name: '0x55e47eeb0b4579748653a796eace4ac2b87a836e30375e2b1a1bdcc81dce86bf' }, { appIndex: 1009, name: '0x978a7fa15bc7d7446919fe923abdb361de0bdf61252fd8db49e805e0f17ec563' } ], onComplete: 0 }
2023-12-10T20:46:55.407Z DEBUG Blockchain-voting on "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA", seed: "0x1ea6cbe0dac0d99beb3903648fc155327c93c870c08106a9b66a7b271e7345d3" (real), VRF proof: "0xd50e00ddaa15a2f5181e46c3910100df4c5808230eef87df14d56ea5a7d40b4a468c5c656f3ec347a5344dc267df2aab6fdc92d711649fe692804c1614b98e47112b67866010c6ac1de6bcf26a51f609", VRF result: "0x8944db7ce5abc02130dcc5bb96ee1c8a7c3a1ee8022b0bfb81b28581764b4695f60dfcaf9ffe2193f538c0df2d43e7b4a9f85a0f4cc12e4dd5d2df8bb0d1f034", request round: "81754", round window: "81755" - "81764"
2023-12-10T20:46:55.418Z DEBUG Calling "voting#1009.vote" by "YHZYUAYUIYNXFMLK5WZ7PYGHVQUIEYULHAKGF5MCYSG76OYP2TYT2WQZRM", id: "68b5c889528b142a", args: { suggestedParams: { flatFee: true, fee: 2000, firstRound: 81755, lastRound: 81764, genesisID: 'sandnet-v1', genesisHash: 'RXrzSgzbMh2FXnMJPwqL2UGeyIdbiks2G1oUvDS7fA8=', minFee: 1000 }, method: 'vote', methodArgs: [ '0x8944db7ce5abc02130dcc5bb96ee1c8a7c3a1ee8022b0bfb81b28581764b4695f60dfcaf9ffe2193f538c0df2d43e7b4a9f85a0f4cc12e4dd5d2df8bb0d1f034', '0xd50e00ddaa15a2f5181e46c3910100df4c5808230eef87df14d56ea5a7d40b4a468c5c656f3ec347a5344dc267df2aab6fdc92d711649fe692804c1614b98e47112b67866010c6ac1de6bcf26a51f609', '0x408f580000000000', '0x4097b00000000000', '0xea1f43d7', '0xcfde068196d23ec21e739eb94d1bb44de9243bd0747024d7d8ae730686b0d675', '0x334143574634484b50544755353535524b4646364b4554533536454f45424f344f534c34425453343658444848494850544e4f42593454525355', '0x3ff0000000000000', '0x49de27a17c0bf466f4bf7c2204905e60627c32a05d32c60f2bf82d73373904eccfde068196d23ec21e739eb94d1bb44de9243bd0747024d7d8ae730686b0d67500500063000000000000000000000000001101000000000000000600000000000000290000', '0x41cdcd6da4000000', '0x3ff0000000000000', '0x00' ], note: '', appID: 1009, sender: 'YHZYUAYUIYNXFMLK5WZ7PYGHVQUIEYULHAKGF5MCYSG76OYP2TYT2WQZRM', boxes: [ { appIndex: 1009, name: '0xd80562f0ea7ccd4ef7b1514be51272ef88e205dc7497c0ce5cf5c673a0ef9b5c' }, { appIndex: 1009, name: '0xa55bf54aa9d489c3395a844d7476efd08296875951191e1b96f35a3cd69a6981' } ], appAccounts: [], appForeignApps: [], appForeignAssets: [], lease: '0x49de27a17c0bf466f4bf7c2204905e60627c32a05d32c60f2bf82d73373904ec' }
2023-12-10T20:47:01.326Z INFO  Submitted 1000004424 vote(s) on request "JHPCPIL4BP2GN5F7PQRAJEC6MBRHYMVALUZMMDZL7AWXGNZZATWA"

If you see log messages with the INFO prefix, but none with DEBUG, then you have not enabled debug logging and need to ensure that you have followed the instructions in the beginning of this section properly. When running an observer node with no stake, it is normal not to see messages after "Using seed...".

Issues with Gora customer applications often crop up at this stage. These are most frequently caused by errors in Gora request encoding or data source specification.

In case of incorrectly encoded request, the node will fail to decode the request correctly and log an error message beginning with Error parsing request.... Make sure you are encoding the request ABI type properly, consulting examples in Developer Quick Start if necessary.

For problems with data sources, examine log messages after Querying..... If there are no errors reported, check debug messages carefully to make sure that data source URLs queried are correct, the content returned is valid and data extraction expressions are matching it as intended. Currently nodes have no way of explicitly reporting failures to customer smart contracts and will simply return an empty result in most scenarios.

Diagnosing issues with destination application

The last phase of processing where a Gora request can fail starts when node voting concludes in consensus and a call is made to the destination smart contract. This may happen because customer's destination app is either specified incorrectly or fails during processing of Gora response.

The destination call is always initiated by just one Gora node. In multi-node Gora networks, it is not possible to reliably predict which one it will be, so one cannot rely on node logs in the this (most common) scenario. The recommended way of debugging such issues is using Developer Quick Start. It provides a local development network with a single node, making the destination call logs always available.

If your application is failing at this stage, examine the error folllowing Calling "voting#... message in your local development node logs. An error occuring inside your destination application will be reported in typical Algorand smart contract error format. Bear in mind, that the destination call is made in an inner transaction inside Gora voting smart contract and interpret TEAL source context accordingly.

To mininize risks of making error in repsonse handling, we recommend using Gora Python library available as a PIP package.

Developer quick start

Developer Quick Start for Algorand is a package of code examples and scripts to help developers start using Gora from their blockchain applications. It contains:

  • Instructions on how to setup and use a local Gora development environment
  • Example applications, also usable as templates
  • Info on commands and tools for troubleshooting your Gora applications

All DQS instructions are written and tested on Linux. Mac users reported success with most of the steps described here and are welcome to follow them at their own risk. Readers must be comfortable with using command-line tools, including tools for blockchain of their choice.

Setting up your Gora development environment for Algorand

There are four essential pieces to a Gora Algorand development environment:

  • An Algorand node providing local simulated Algorand network
  • Algorand Python libraries for smart contracts and blockchain APIs
  • Deployed Gora smart contracts
  • A Gora development-only node running and connected to the above

The following Algorand software must be installed and functioning:

Refer to documentation at the above links for download and installation instructions. If using a different package to setup your Algorand node, such as AlgoKit, find out its Algod API connection port number and have it handy. If it differs from 4001, you will need to enter it during setup of Gora software.

To install and configure Gora software for your development environment, run python3 setup.py and follow the prompts. Gora tools will be downloaded and config files created for you automatically in the checkout directory.

When the above script finishes, you will have Gora smart contracts deployed to local network in your Algorand Sandbox install and a Gora node set up for them. This will form a local development-only single-node Gora network necessary to serve your locally tested applications.

For local oracle requests to be served, your development Gora node must be running whenever they are made. There are two ways to ensure this. One is to run it temporarily from a script that executes your application test cycle. This is what example apps in this repository do; details can be gleaned from their source code. Another way is to run the node continuously for the duration of your development session. To start it with output to the terminal, change to the checkout directory and run: GORA_CONFIG_FILE=./.gora ./gora_cli docker-start. To make it run in the background, add --background switch to the above command. To see node's log messages, run docker logs gora-nr-dev.

Example applications

This repository includes several example PyTeal applications demonstrating the use of Gora oracle. They will be considered below in the order of complexity. Example apps are built with Algorand's Beaker framework and are commented to make them accessible for novice developers.

To run an example app, execute it with Python, e.g. python example_const.py. You should get an output like:

Loading config from "./.gora"
Main app ID: 1004
Using local account ETKGKDOICCD7RQRX7TX24RAAM2WTHP7L4EGIORVLJEKZO7FWNY27RUTF3E
Deploying the app...
Done, txn ID: 3GH2465S6GPWRGHZQPHRQ7SHU7YOLXVPQVY64IJM2PVF4MSBM57A
App ID: 1280
App address: DPF45GKEB2H7P7HJNRHYNJXZTCSPWMLBIOFR5ZM6V2FJTPMNJ7C2VBQRHA
Token asset ID: 1003
Initializing app for GORA...
Setting up Algo deposit...
Setting up token deposit...
Calling the app
Confirmed in round: 16598
Top txn ID: USH3IB32OH5QQHGKHQGWLTW46QCOEKWQGCJ472G6FXG2VG2LLHPA
Running: "./gora docker-status"
Background development Gora node not detected, running one temporarily
Running: "./gora docker-start"
Gora CLI tool, version N/A
gora-nr-dev
2023-11-13T13:28:26.679Z DEBUG Applying GORA_CONFIG environment variable
2023-11-13T13:28:27.557Z INFO  Starting Gora Node Runner
2023-11-13T13:28:27.909Z INFO  Version: "1.1.30"
2023-11-13T13:28:27.909Z INFO  Built on: "Sat, 11 Nov 2023 22:07:48 GMT"
2023-11-13T13:28:27.909Z INFO  Revision: "59652555bf372e85185d8cad47b99d3a8eb032ea"
2023-11-13T13:28:27.909Z INFO  Smart contracts revision: "1535e07cc84cdfea2ac8d0ec4bcb854c9f7d21ba"
2023-11-13T13:28:27.909Z INFO  Docker image: "107782235753.dkr.ecr.eu-central-1.amazonaws.com/gora-nr:v1.1.30"
2023-11-13T13:28:27.910Z INFO  Docker image hash: "705d77c0330c8a1ddd07c1c2618e0ca5cf1debd583e4fa0b49d9f4fa2398a07b"
2023-11-13T13:28:27.986Z DEBUG Blockchain server host is local, changing it to "host.docker.internal" to make it work under Docker
2023-11-13T13:28:28.151Z INFO  Using Algorand API server: "http://host.docker.internal:4001/", port: "4001"
2023-11-13T13:28:28.191Z DEBUG Block seed is available
2023-11-13T13:28:28.195Z DEBUG Using network config override
2023-11-13T13:28:28.258Z INFO  Main address: "I5EY62R2X5PSONSKWEEXZAUC5WZ3XQZPUQOA2RQKLFNKKKM5BPXWN7EFEQ"
2023-11-13T13:28:28.258Z INFO  Participation address: "MA2XUHMW4F2HWJSXMX6GVFJSV4QDJLS4U2HDELEX75QQH2YE4LZSMVZIOE"
2023-11-13T13:28:28.259Z INFO  Main smart contract: "1004"
2023-11-13T13:28:28.259Z INFO  Voting smart contracts: "1010, 1014, 1018"
2023-11-13T13:28:28.259Z INFO  Token asset ID: "1003"
2023-11-13T13:28:28.261Z INFO  Last blockchain round: "16600"
2023-11-13T13:28:28.266Z INFO  Staked amount: 1000000000000 microGORA
2023-11-13T13:28:28.266Z INFO  Deposits: 70000 microALGO, 7000000000 microGORA
2023-11-13T13:28:28.324Z INFO  Oracle sources set up: 31
2023-11-13T13:28:28.401Z INFO  Processing round "16598" only
2023-11-13T13:28:28.419Z DEBUG Handling call "main#1004.request" from "DPF45GKEB2H7P7HJNRHYNJXZTCSPWMLBIOFR5ZM6V2FJTPMNJ7C2VBQRHA", round "16598"
2023-11-13T13:28:28.423Z INFO  Processing oracle request "2L7P3TYMSNBMBGMW2RFZESVXYB4W5NFH42KG5GBTU6UY53ZBIOIQ", destination: "1280.handle_oracle_const"
2023-11-13T13:28:28.424Z DEBUG Querying source #1, args:
2023-11-13T13:28:28.424Z DEBUG Result #0, source "1": 1, for "2L7P3TYMSNBMBGMW2RFZESVXYB4W5NFH42KG5GBTU6UY53ZBIOIQ"
2023-11-13T13:28:28.425Z DEBUG Result for "2L7P3TYMSNBMBGMW2RFZESVXYB4W5NFH42KG5GBTU6UY53ZBIOIQ": 1 (number, single)
2023-11-13T13:28:28.433Z DEBUG Using seed: "0x9d2b280c6aacbff4357c2f1fc0ba0e94f46160f4a2368f763a947944878abc86"
2023-11-13T13:28:28.438Z DEBUG Alloted "999982301" vote(s) for "2L7P3TYMSNBMBGMW2RFZESVXYB4W5NFH42KG5GBTU6UY53ZBIOIQ", zIndex: "4"
2023-11-13T13:28:28.456Z DEBUG Creating verify txn to vote on "2L7P3TYMSNBMBGMW2RFZESVXYB4W5NFH42KG5GBTU6UY53ZBIOIQ": { suggestedParams: { flatFee: true, fee: 0, firstRound: 16599, lastRound: 16608, genesisID: 'sandnet-v1', genesisHash: 'RXrzSgzbMh2FXnMJPwqL2UGeyIdbiks2G1oUvDS7fA8=', minFee: 1000 }, from: 'GBS6GNRJIOD3SFHQGCXT7QBUF2V6G7HHG7J3M3XYSAF57FIN4RN53DTRTU', appIndex: 1004, appArgs: [ '0x23fd2961', '0x46a261eaa8af75c2af39cc8232d849fb77def96e264a6fb02b14e5563a2e9ac5ff3513bc613405c6461898523280e17596543f4da7461910f4cb8662b6437d87', '0xf02acf8d37b7ae2d55be012bebbaab21322aea4ec214c5ba5b1def593906b29c1949842a74e904b7b7030ab6d003e6ccebab7efa7e7fa897a09e6bdc4cfac4eb9f11f6930761335de7b57f0643dd4108', '0x9d2b280c6aacbff4357c2f1fc0ba0e94f46160f4a2368f763a947944878abc86', '0x0000000000000001', '0x0000000000000002', '0x0000000000000003', '0x0000000000000001', '0x8ca52b2d1e080d74325852bf3d76bd6a8c4b335c198cab591e67df8e27476e6a', '0x3a66f2b5dba56c2aac3705641397a3aa5c8ee4f5c3ee84877e80346a9cb48bb58d6884389847ca9e3d115a7fdac390ac42b04ed8a1cb57c532181afe549ccebe000000003b9b31b5000000e8d4a51000000000000000000800000000000000009ddd285c2891cb74b21b2bbada87c4298459c3367a477eb3be6f00e5cb8eb2ae80', '0x0000000000000004' ], accounts: [ 'MA2XUHMW4F2HWJSXMX6GVFJSV4QDJLS4U2HDELEX75QQH2YE4LZSMVZIOE', 'I5EY62R2X5PSONSKWEEXZAUC5WZ3XQZPUQOA2RQKLFNKKKM5BPXWN7EFEQ', 'VSFZF5BRBVJY7P5QQN73JQ27DX3RP6PWSHW4I3SFFFZYFNTGCM3ZC2DHLE', 'TXOSQXBISHFXJMQ3FO5NVB6EFGCFTQZWPJDX5M56N4AOLS4OWKXAPCZFSY' ], foreignApps: [ 1010 ], boxes: [ { appIndex: 1004, name: '0x8ca52b2d1e080d74325852bf3d76bd6a8c4b335c198cab591e67df8e27476e6a' }, { appIndex: 1004, name: '0x3a66f2b5dba56c2aac3705641397a3aa5c8ee4f5c3ee84877e80346a9cb48bb5' }, { appIndex: 1010, name: '0x8d6884389847ca9e3d115a7fdac390ac42b04ed8a1cb57c532181afe549ccebe' } ], onComplete: 0 }
2023-11-13T13:28:28.463Z DEBUG Blockchain-voting on "2L7P3TYMSNBMBGMW2RFZESVXYB4W5NFH42KG5GBTU6UY53ZBIOIQ", seed: "0x9d2b280c6aacbff4357c2f1fc0ba0e94f46160f4a2368f763a947944878abc86" (real), VRF proof: "0xf02acf8d37b7ae2d55be012bebbaab21322aea4ec214c5ba5b1def593906b29c1949842a74e904b7b7030ab6d003e6ccebab7efa7e7fa897a09e6bdc4cfac4eb9f11f6930761335de7b57f0643dd4108", VRF result: "0x46a261eaa8af75c2af39cc8232d849fb77def96e264a6fb02b14e5563a2e9ac5ff3513bc613405c6461898523280e17596543f4da7461910f4cb8662b6437d87", request round: "16598", round window: "16599" - "16608"
2023-11-13T13:28:28.478Z DEBUG Calling "voting#1010.vote" by "MA2XUHMW4F2HWJSXMX6GVFJSV4QDJLS4U2HDELEX75QQH2YE4LZSMVZIOE", id: "23e1ed9b96248aff", args: { suggestedParams: { flatFee: true, fee: 2000, firstRound: 16599, lastRound: 16608, genesisID: 'sandnet-v1', genesisHash: 'RXrzSgzbMh2FXnMJPwqL2UGeyIdbiks2G1oUvDS7fA8=', minFee: 1000 }, method: 'vote', methodArgs: [ '0x46a261eaa8af75c2af39cc8232d849fb77def96e264a6fb02b14e5563a2e9ac5ff3513bc613405c6461898523280e17596543f4da7461910f4cb8662b6437d87', '0xf02acf8d37b7ae2d55be012bebbaab21322aea4ec214c5ba5b1def593906b29c1949842a74e904b7b7030ab6d003e6ccebab7efa7e7fa897a09e6bdc4cfac4eb9f11f6930761335de7b57f0643dd4108', '0x408f600000000000', '0x4094000000000000', '0xbbdd1de0', '0x1bcbce99440e8ff7fce96c4f86a6f998a4fb3161438b1ee59eae8a99bd8d4fc5', '0x4935455936325232583550534f4e534b574545585a41554335575a3358515a5055514f413252514b4c464e4b4b4b4d35425058574e3745464551', '0x3ff0000000000000', '0xd2fefdcf0c9342c09996d44b924ab7c0796eb4a7e6946e9833a7a98eef2143911bcbce99440e8ff7fce96c4f86a6f998a4fb3161438b1ee59eae8a99bd8d4fc500500063000000000000000000000000001101000000000000000100000000000000000000', '0x41cdcd426e800000', '0x4010000000000000', '0x00' ], note: '', appID: 1010, sender: 'MA2XUHMW4F2HWJSXMX6GVFJSV4QDJLS4U2HDELEX75QQH2YE4LZSMVZIOE', boxes: [ { appIndex: 1010, name: '0x47498f6a3abf5f27364ab1097c8282edb3bbc32fa41c0d460a595aa5299d0bef' }, { appIndex: 1010, name: '0x6e8e497a81d11786378b1419468bf2315758b0e1b6bfc4ecd4c8837bd48580f0' } ], appAccounts: [], appForeignApps: [], appForeignAssets: [], lease: '0xd2fefdcf0c9342c09996d44b924ab7c0796eb4a7e6946e9833a7a98eef214391' }
2023-11-13T13:28:34.589Z INFO  Submitted 999982301 vote(s) on request "2L7P3TYMSNBMBGMW2RFZESVXYB4W5NFH42KG5GBTU6UY53ZBIOIQ"
Waiting for for oracle return value (up to 10 seconds)
Received oracle value: 1.0

Note the last line: Received oracle value: 1.0. It shows the value returned by the oracle which has been successfully processed and stored by the executed app. If your Gora development node is already running, the date-prefixed log messages above will be found in its output rather than in script's output above. Let us now look at example apps in more detail.

Basic example: example_const.py
Demonstrates the use of Gora in most simple and detailed way. It makes a query to a special built-in test source which always returns the value of 1. The request is prepared without using Gora support libary, to make the process more explicit. Since no external sources are queried, this example can even be run offline.
Classic example: example_classic.py
Demonstrates the use of Gora with predefined data sources. These sources are pre-configured under fixed numeric ID's, with more of them potentially being added in future releases. This and following examples use Gora Python library to simplify oracle request building. This is the recommended approach for production use. The classic example queries two currency rates and returns the maximum of the these. The source #7 is queried in both cases, but different sources can be used just as well. Source #7 uses a paid data provider that requires authentication. It is enabled by specifying ##signKey as the second source parameter which makes Gora nodes generate a temporary signature key bound to the request and the node. The data source server being queried will check the validity of the request and the node's stake on the blockchain. This allows opening a data source to Gora users only without requiring them to provide authentication data or exposing it on the blockchain.
General URL example: example_url.py
Shows how to use Gora for fetching data from arbitrary URLs. Data from URL responses can be extracted with a variety of methods such as JSONPath, XPath, regular expressions, or substring specifications. Two key differences from the previous example are the request parameters and dummy method calls. The latter is necessary for increasing op code budget of the request transaction, to accomodate the needs of more the flexible request type. To issue such requests with more URLs, you may need to add more do_nothing_N() dummy methods and increase 4th parameter of run_demo_app() to raise op code budget even more.
Off-chain computation example: example_off_chain.py
Demonstrates Gora's arbitrary off-chain computation capability. It takes a UK postal code, geolocates it via third-party free API, then queries another free API and returns current air temperature at the location. The C source code accomplishing this can be found in example_off_chain_multi_step.c. The WebAssembly resulting from compilation of this code is included in the example app verbatim, as a byte string constant. For more off-chain executable specification options as well as other details on the off-chain computation feature, refer to Gora off-chain API documentation.

Troubleshooting

Algorand Dapp Flow web app can be used to inspect related application transactions.

To connect it to your local Algorand network, open the drop-down menu under the logo in the top left cornet and select "Sandbox". Use application or transaction IDs from the tested app output to find and inspect transactions of interest.

Running Gora nodes

Gora node operation workflow and options

Gora is a decentralized blockchain oracle. To produce its values, it relies on a network of independent blockchain-connected nodes running Gora Node Runner (GNR). Gora token issuance and distribution, required to bootstrap consensus verification, are usually done once by the network owner and are beyond the scope of this discussion. For the purposes of this document, setting up a Gora node network comes down to setting up GNR for each node operator.

The GNR is distributed as a Linux-based Docker image that runs on any docker-enabled customer host or in Amazon cloud as an AWS Fargate application. GNR is managed via Gora CLI tool - a self-contained command-line executable that encapsulates all required functionality for Gora node operators or power users.

Prerequisites

To become a Gora node operator, one should possess the following:

  • Ability to work with command-line (CLI) tools and Linux OS in general. Advanced customization may require editing of text files in JSON format. If you are not comfortable with the above, it is recommended that you enlist professional help or get up to speed using resources widely available online.
  • A 64-bit x86 Linux host with Docker installed, or an AWS account. Gora software is not thoroughly tested on Mac OS at this time, but some users reported success with Linux instrucitons, so others are welcome to follow them at their own risk.
  • Access to a live API node for each blockchain network they are going to support. Gora serves multiple blockchains, so access options vary per chain. Some are listed in Appendix 1 at the end of this document. In any case, you can run a blockchain node yourself which many Gora node operators do.
  • A funded blockchain account with a wallet connected to it for each supported blockchain network. It is used for opting into Gora applications, receiving Gora tokens and staking them.

Installing Gora Node Runner

1) Install required software

  • Install Docker engine if you want to run your node locally, or AWS CLI if you prefer to run it on AWS. When choosing Docker, ensure that you are able to use it as a normal, non-root user; normally this requires adding yourself to the docker group, then logging out and back in.
  • Download Gora CLI tool. To do it with wget utility run: wget https://download.gora.io/latest-release/linux/gora -O gora
  • Make the downloaded binary executable by running chmod u+x ./gora

2) Initialize your installation

To kick off the initialization process, run ./gora init. You will be guided through the steps with prompts and messages. You can abort the process at any time by pressing Ctrl-C. During initialization, Gora CLI tool will:

  • Create your Gora participation accounts on the selected blockchains.
  • For blockchains that require it, link your participation and main accounts, and supply it with Gora tokens and currency.
  • Create ~/.gora configuration file with info on used accounts as well as access details for blockcians API nodes that your Gora node will use.

To start over, rename or delete the produced config file ~/.gora.

3a) Start your Gora node locally

Before continuing, make sure that you have installed Docker and added yourself to the `docker` group on your host. To start your node, execute: ./gora docker-start. When run for the first time, it will pull GNR docker image from Gora docker registry. As the node launches, it starts logging its progress to standard output, e.g.:

2024-10-04T11:19:57.613Z INFO  Starting Gora Node Runner
2024-10-04T11:19:57.984Z INFO  Version: "1.1.65"
2024-10-04T11:19:57.984Z INFO  Built on: "Sat, 02 Nov 2024 02:54:17 GMT"
2024-10-04T11:19:57.984Z INFO  Revision: "f9e66e7918f43326974d7ac345bec4bee734a0df"
2024-10-04T11:19:57.984Z INFO  Smart contracts revision: "ac9e053d68dd6718c02ed36b76cd9ba478a390bb"
2024-10-04T11:19:57.984Z INFO  Docker image: "107782235753.dkr.ecr.eu-central-1.amazonaws.com/gora-nr:v1.1.65"
2024-10-04T11:19:57.985Z INFO  Docker image hash: "dd20b9d55e7081687b3cacfba9d1e93a49e924c4e8e7a582cbedc86b6285c55d"
2024-11-04T11:19:58.951Z INFO  EVM network "baseMainnet" enabled, chain Id "8453", main contract "Gora" at "0xd4c99F88095F32dF993030d9a6080e3BE723F617"
2024-10-04T11:20:01.020Z INFO  Waiting for next oracle request

To make the node switch into background upon startup, add --background to the command. For a quick check of instance's status, use gora docker-status. To inspect instance's logs, use docker log command, e.g.: docker logs -fn 100 gora-nr.

3b) Start your Gora Node on AWS

Running your Gora node on AWS is a lower maintenance option, altough it is not as flexible or economical as running it locally. To begin, download and install AWS CLI. You should then be able start your Gora node right away by running ./gora aws-start. During a first-time execution, several AWS configuration items will be set up for you, producing an output like:

Creating security group "gora-nr-sg"
Creating log group "gora-nr-logs"
Registering task definition "gora-nr-task"

Then you should see the kind of output that would appear every time you start your AWS Gora node up:

Startup initiated, task ID: "2468d56dff884c9ca536fb2e537f8928"

This means that AWS has been asked to start your node up and it should be online shortly. You can check its current status by executing ./gora aws-status which should eventually produce an output like:

State: Running
Started at: 2022-07-04T17:33:08.803Z
Uptime: 2 min.
Task ID: "2468d56dff884c9ca536fb2e537f8928"

This confirms your Gora node has been started by AWS. To check up on it, you can always inspect its logs via AWS web UI or by running ./gora aws-log.

4) Stop your Gora node

To stop a Gora node running locally in the foreground, hit Ctrl-C. If it is running in the background, execute ./gora docker-stop. To stop a node on AWS, run ./gora aws-stop.

Updating or moving installations

Gora CLI tool is updated with gora update command. It checks whether there is a more recent version than the one being run, and if so, offers to upgrade it by downloading and replacing the gora binary. Current binary will be backed up. GNR is distributed as a docker image, so it will be automatically updated whenever your Gora node is started. To ensure that you are running the latest version, simply stop and start your node again.

Your can move your Gora installation to a new server without setting it up from scratch. Copy the gora binary to a new location of your choice and ~/.gora configuration file to your home directory on the new server. Make sure you have Docker installed and enabled for the user that runs Gora node on the new server as well. When you start your node for the first time at the new location, it may take some time to fetch the GNR docker image. Make sure not to run multiple nodes off the same configuration at the same time.

Gora node configuration

A Gora node configuration is defined by the blockchain accounts it is linked to as well as various customizations via configuration variables. These are set during the initialization process described above and usually do not need to change. But for basic troubleshooting or developer customization purposes, here is an overview.

Gora config file contains settings specific to your Gora node in JSON format. When deploying a node to cloud or launching it locally, Gora CLI tool reads this file and passes its contents to GNR via Docker container environment settings. This makes local configuration available to GNR without using docker mounts. The default location of Gora config file is ~/.gora.

Appendix

Value extraction specifications

A value extraction specification tells oracle how to extract a specific piece of data from a response returned by the data source. It consists of up to three parts, separated by colons: method, expression and an optional rounding modifier. For example, substr:4,11 tells Gora that it needs to return a substring from data source output, starting at 4th and ending at 11th character.

Gora supports several extraction methods and expression formats:

Expression type Example Best for
JSONPath jsonpath:jsonpath:$.data.temperature JSON documents
XPath xpath:/p/a XML documents
Regular expression regex: the magic number is ([0-9]+) Structured text
Character Substring substr:0,10 Unstructured text
Byte fragment bytes:2,4 Unstructured binary data

An optional rounding modifier is used to round floating-point values to certain amount of digits after the point. This may be necessary with some types of values such as cryptocurrency exchange rates. They can be so volatile that different Gora nodes are likely to get slightly different results despite querying them at almost the same time. That would prevent the nodes from achieving consensus and confirming the value as authentic. Adequate rounding gets us around this issue.

For instance, if you specify jsonpath:$.rate:3, the responses { "rate": 1.2344 } and { "rate": 1.2342 } that may be received by different Gora nodes will yield the same value "1.234". The nodes will achieve consensus and you will get "1.234" as the resulting oracle value. Rounding only affects fractional part of the rounded number, all whole part digits are preserved. For example, if rounding parameter is set to 4, the number 1.12345 will be rounded to 1.1234; but, for exmaple, the number 12345678 will remain unaffected.

Roadmap for 2025

Gora is always working to improve its core product as well as cater for new market demands. In 2025, we plan to accomplish the following goals:

Browser-based Gora nodes

Currently running a Gora node requires a Linux server or an AWS cloud account. Soon we will make it possible with any Javascript-enabled web browser. This will allow anyone with a web3 wallet to instantly become a node operator, earning rewards and strengthening the distributed nature of Gora node networks. ASO customers will be able to get started much more easily with their own node networks too. We have been working on making our node software codebase browser-compatible for a while and we are now finalizing this work.

Solana support

Solana usage has grown tremendously in recent times. We are now building proof-of-concept implementations of our key components and will soon be prepared to fully tap into that ecosystem.

Gora mobile app

Building on the platform-agnostic nature of our revamped codebase, we are planning a React Native superapp. It will provide a single point of control for all Gora functions: a local Gora node, Gora-specific blockchain explorer, ASO control panel and server node deployments manager. Apart from user convenience, it will bring some unique opportunities such as direct feeding sensor data from any IOT or mobile device into blockchain.

Automated executor deployment

Currently ASO customers willing to deploy their own executor oracles must follow a semi-manual process with the help of our staff. We are planning to make it fully automated using a factory smart contract.

Legacy documentation (Litepaper)

Introduction

Gora aims to accelerate the development of dApps that are useful in the day to day lives of millions of users. Gora will accomplish this by providing the infrastructure necessary for developers and organizations to build applications that make use of real world, off-chain data. Furthermore, Gora will enable developers to build applications that utilize off-chain computation.

In 2022, the number of daily users for decentralized applications surpassed 2.4 million daily users, with several billion dollars in volume. Global uncertainty has lead to increased adoption of cryptocurrencies, especially in emerging markets. This increased adoption of cryptocurrencies has familiarized people with wallets and the mechanics of transacting on-chain. As the general population becomes more comfortable using decentralized applications, demand for oracles to provide real world data will see a similar rise.

Along with record numbers of new users, the blockchain space has also seen record number of hacks and exploits, many of these being through bridges/oracles hacks and manipulation. This highlights the need for security as a top priority for any oracle solution such as Gora.

The primary goal of this litepaper is to highlight the organization and backers of Gora, and to highlight compliance of the protocol. In addition, it will aim to provide a high level overview of the functioning mechanism of the oracle, and the types of feeds Gora plans to launch with. This litepaper is accompanied by an in-depth economic design paper. Technical details of the network will be released in a future whitepaper closer to the launch date of the protocol.

The Gora Vision

The Gora vision is to deliver an Oracle solution that brings mainstream adoption to web3, and empower an ecosystem of decentralized applications to solve real-world user problems.

Our mission is to advance the state-of-the-art in oracle and blockchain reliability, safety, and performance by providing a flexible and modular oracle architecture. This architecture should support frequent upgrades, fast adoption of the latest technology advancements, and first-class support for new and emerging use cases.

We envision a decentralized, secure, and scalable network governed and operated by the community that uses it. When infrastructure demands grow, the computational resources of the oracle network scale up horizontally and vertically to meet those needs. As new use cases and technological advances arise, the network should frequently and seamlessly upgrade without interrupting users. Infrastructure concerns should fade into the background, and security should be guaranteed without trading off privacy or decentralization.

To achieve this Gora will:

  • Hire and partner with highly skilled computer scientists, cryptographers, mathematicians and statisticians, data scientists and more
  • Build a modular application that allows development and upgrades of specific modules
  • Work with 2-3 audit firms to review Gora's Code
  • Perform millions of simulations that model the trajectory of the token based on starting parameters

Design Considerations

Layer 1 vs Layer 2

Oracle services tend to generally build out co-chains or blockchains to be able to handle requests, and then interface onto one or more blockchains. While this method allows for growth beyond a single blockchain, the technical infrastructure required is greater than building specifically for a single blockchain.

When designing Gora, we made the intentional choice to focus on building an independent network. Therefore, GoraNetwork may be categorized as a Layer-2 network, with a distributed set of nodes. This means a much faster time to market, and a greater focus on security by reducing the number of attack vectors than building GoraNetwork as a Layer 1 network. This does not limit GoraNetwork, as the distributed nodes can interface with other blockchains as necessary.

True Decentralization

Another consideration in the level of decentralization (aka permission to participate). Although GoraNetwork is not in and of itself a blockchain, it is very similar in that it is a distributed system of nodes and data providers, working together to affect the blockchain state - and as such, a level of permission, if any, needs to be determined.

On one spectrum, an permissioned Oracle service can require knowledge the feed provider is, and only a select number of these known feed providers to submit feeds. On the other end of the spectrum, a permissionless service will neither require nor care who signs up to provide feeds.

In their article “When Permissioned Blockchains Deliver More Decentralization Than Permissionless” (Bakos et al.)[1], the authors make a compelling argument that “while distributed architectures may enable open access and decentralized control, they do not preordain these outcomes…permissionless access may result in essentially centralized control, while permissioned systems may be able to better support decentralized control”. What this means is just because a system is built to be decentralized and distributed, it will take time to get there, largely due to malicious forces or large actors with economies of scale taking over at before the network is able to achieve network effects.

The Gora team believes that decentralizing as much as possible is essential but agrees with the authors above that some form of permission is necessary, especially at the beginning. This is implemented via a deposit mechanism, where Node runners must stake and deposit a certain amount of network tokens to participate. This raises the barrier to entry, with the goal being to make being a malicious actor or a poor-quality provider as unattractive as possible. Furthermore, aggregated data feeds will allow community members to vote in or vote out feed providers. This way, only feed providers that are institutional and can guarantee high quality data are allowed to participate (unless the community decides otherwise).

The Oracle Problem

The oracle problem, at its core, means having to trust an entity in a world that should be considered trustless. It could be argued that the only real solution to this problem would be to conduct all activities that an oracle provides onto the blockchain. For example, if ALGOs were only ever exchanged for USDC on-chain, and USDC was only ever spent by individuals on chain, the exchange price of USDC/Algo may be determined on the blockchain, hence solving the oracle problem. However, basketball cannot be played on the blockchain, nor does the blockchain have a weather system.

One possible solution might be having everyone watching the game input the score on the blockchain, but what if the loser of a match has more fans, and feel like they deserved a penalty? With the advent of social media, coordinated social ‘spamming’ a system can and does go viral such as the ‘Bum rush the charts’ campaign to influence iTunes charts[2], or meme-fueled GameStop buying frenzy meant mainly to bankrupt large hedge funds[3].

By such definitions, the oracle problem may be considered unsolvable. While a detailed analysis of the oracle problem is outside the scope of this document, an alternative to such a strict definition is that as long as data is sourced from multiple reliable sources, and poor quality or malicious participants stand to lose much more than they gain (in addition to having a high cost barrier to entry), the system should remain secure. In fact, almost all major blockchains are designed as such.

Jurisdiction

Gora is incorporated in Switzerland, in the city of Zug, also known as Crypto Valley. Switzerland became one of the first countries in the world to enact legal regulations for blockchain technology, creating legal certainty for developers, customers and investors. The integrity of the financial regulatory framework makes Switzerland the gold standard among jurisdictions, and cryptocurrencies are subject to the same rules as real monetary assets.

Gora plans to eventually be a fully decentralized protocol, where the organization has none to little say in the direction of the protocol. However, during the first 1-2 years of operation, the core team will directly affect the direction of the protocol, and as such requires the team to be compliant with local and international regulations, especially regarding the handling of monetary investments.

Team

Gora is comprised of both full time and part-time contractors, and utilizes strategic advisors to help build the protocol. This section describes our team members.

Management

Abdul Osman - CEO
Gora is led by CEO and founder Abdul Osman who has a background in Software Engineering and Business Administration. He is the founder of two software companies with Gora being his second successful venture. He specializes in creating innovative web and mobile applications with over 8 years of experience in bringing technological products from ideation to delivery to scale. Under his vision Gora aims to be the the first enterprise grade oracle network to bring proper decentralization, speed, security, and off-chain computation to Algorand.
Ali Hassan - CFO
Finance is managed full time by CFO Ali Hassan who has over 15 years’ experience working in financial analysis, internal audit & risk management for several multinational organizations. Ali ensures that grant, seed funding and treasuries are managed to the highest standards and that Gora is in compliance with accounting regulations and financial reporting mandates.
Chris Brookins - Head of Business
Chris has 10+ years experience in credit, blockchain, technology, and machine learning. Chris co-founded RociFi Labs, a blockchain and machine learning development company that built RociFi, an under-collateralized lending and credit scoring protocol on Polygon & zkSync; and RA, an onchain wallet analytics tool.
Joseph Jones - CTO
The engineering department is led by Joseph Jones who is a multifaceted software engineer– having worked in many areas of software including machine learning, DevOps, web/mobile applications and blockchain development. He leads a team of six, devising the technical strategy to ensure Gora’s technology is not only cutting edge but also in alignment with its long-term business goals.

Engineering

Julius Githaiga - Full Stack developer
Julius Githaiga is a highly skilled full stack developer with a decade of experience working in senior web development roles. He loves working on complex web development projects making him the perfect fit to design Gora’s web application to guarantee it delivers both functionality and an excellent user experience.
Egor Shipovalov - Distributed Systems Engineer
Egor is a software developer and published researcher with over two decades of experience in various senior software development roles (e.g. Amazon, Deutsche Bank). His work has been crucial to the development of Gora’s Node Runner software.
Samantha Palmer - Blockchain Developer
Samantha Palmer is a highly skilled Computer Scientist with experience as a Lead Data Scientist and Software Engineer in the past and currently uses her skills at Gora to develop, test, deploy and maintain smart contracts and other blockchain interfacing functionality.
Jesse Wallace - Blockchain Developer
Jesse Wallace is an Electrical Engineer with extensive experience in hardware and software development. Jesse is an integral part of Gora, writing, testing and maintaining smart contracts.
George Njuguna - Senior Backend developer, Integration specialist
George Njuguna has more than 10 years of experience in Systems Development with an expertise in project management and strategic planning. George is key in ensuring data feeds from over 50 data providers are seamlessly integrated into the Gora protocol.

Data Science

Ahmed Ali - CDO
Data management is led full time by Ahmed Ali who has worked as a lead statistician and data scientist in the past and holds a masters in Mathematics and Statistics. His work has been key to creating models and simulation methods to determine the most suitable rewards system for our ecosystem participants.
Stylianos Kampakis - Data Scientist (Part Time)
Dr. Stylianos Kampakis has a PHD in Computer Science and has over a decade of experience in Data Science, specifically in the Blockchain industry. He is an expert in designing tokenomics and his work has been integral to the formation of Gora's tokenomics. Dr. Stylianos has also published several books and papers on the topics of data modelling, blockchain, and various data science relatied topics.
Marketing and Admin Fred Arias - CMO
Fred is an award-winning creative director, with a career in Television and Media spanning 20 years. Fred most recently completed a Masters in Marketing management, with a thesis in Marketing Strategies for Web3. Furthermore, Fred has a track record of executing under pressure, and bringing teams and communities together to achieve a common goal.
Adam Kinley - Director Of Strategic Partnerships
Adam Kinley is an experienced business development account executive and consultant and is responsible for increasing brand awareness for Gora and developing strategic partnerships. He accomplishes this by meeting with investors, onboarding institutional feed providers and educating developer teams on network functionality, feed providers/tiers, staking options, validator nodes, and our overall tokenomics.
Andre Bussanich - Creative Director
Andre Bussanich is an award winning storyteller with over two decades of experience working as a Creative Director in advertising, video animation, and social media marketing. Using his unique set of skills, he directs all creative projects and shapes the standards of our brand identity at Gora.
Amal Osman - Social Media Coordinator/Digital Producer
Amal Osman has worked as a videographer, photographer, and podcast producer in the past and uses her creative skills to produce high quality videos and other social media content. Working closely with our creative director Amal is tasked with positioning Gora as a hub for all types of Oracle and Blockchain related content.

Advisors

Gora is advised by several experts with a wide range of skills. The section below describes our appointed advisors.

Olu Omoyele
Olu is a governer at Algorand, graduate of Oxford, Harvard and MIT and Executive Leader with nearly two decades of experience in Financial Regulatory Policy & Risk Management in both the public and private sectors. He has worked for the Bank of England, Bank of America Merrill Lynch and Visa in the past. He also serves as an advisor for multiple Fintech startups and Blockchain companies. He provides a valuable blend of financial advice from both traditional and decentralized finance.
dxFeed
dxFeed offers the broadest range of data services currently available by a single company in the financial space and has built one of the most comprehensive ticker plants in the world. They are a subsidiary of Devexperts, who specialize in providing financial markets information and services to buy-side and sell-side institutions of the global financial industry, specifically to traders, data analysts, quants and portfolio managers. dxFeed's will provide high quality data to Gora and advise the technical team on how to most effectively format the data that Gora obtains for the dApps on the Algorand blockchain.
Brave New Coin
BNC provides data, analysis and research to a global network of market participants. BNC's experience and expertise make them the leading provider of standard and non standard institutional grade, highly compliant, data solutions. Brave new coin advises Gora on data strategy, as well ad advertising through their media networl.
Patrick Sibetz
Patrick is a quantitative analyst with an expertise in equity and derivatives quantitative trading. He assists Gora with token modelling.
Thomas Melskens
Thomas Melskens is a pioneer in the blockchain space with a long history of involvement and leadership in the industry. Melskens has made significant contributions to the development and adoption of blockchain technology and has played a vital role in shaping the direction of the industry.