Smart Contracts

A particular block body data structure with special instructions to be executed.

Compute power.

Regardless of the implementation, any block where the 'blockContent' property starts with // is interpreted as a smart contracts and its content will be executed with a JavaScript interpreter. All smart contracts must start with a comment on the first line as shown below:

// <DESCRIPTION?> || <CONTRACT_INVOCATION(HASH/ALIAS)

When declaring a smart contract, this line is commonly used to provide a description or version information. However, when invoking a smart contract, this line must be filled with either the contract hash or the contract alias, if it has been set previously.

The scope

A core concept within NxtFi, the scope is regarded as a branch of the tree-like framework architecture. It is an essential component of the hierarchical structure that empowers the network's most robust features. In its simplest and most practical form, the scope is defined as the pathway to the organization of the smart contracts domain. It serves as a directory to organize code, state, and their interactions. But they are not arbitrary in every sense, meaning that a scope is either the name of the entity who is signing, or a contract hash identifier, or even a child entity authorized by its parent through the grantWrite authorization process.

Usage

The first stage when declaring a smart contract involves following simple syntax rules. As mentioned earlier, it is mandatory to begin with a commented headline. Following that, the execution logic needs to be implemented. This includes:

  • Inputting Parameter and Variable declarations (JSON inputs are allowed to interact with SC).

  • Access Control Layer system middleware (ACL to hide sensitive data or to serve data directly from an API).

  • Smart Contract code logic.

  • Smart Contract Alias setters (directly set a SC alias in deploy to initialize a custom/dedicated scope).

The code will be executed every time the smart contract is invoked, in a synchronous loop. It is important to consider the initial execution (when the SC is deployed for the first time) and handle all possible exceptions to prevent crash faults.

Whenever the execution thread is blocked due to an asynchronous call, a crash fault, or an infinite loop, the interpreter is not able to distinguish between them. Consequently, the code will be executed again until the code resolves or a specific time interval (δ) has elapsed. Therefore, it is important to be cautious and avoid infinite loops, as they can cause the block to fail.

Smart contract Call

After declaring and propagating (deploy) a smart contract code block on the NxtFi blockchain, it becomes ready for usage and execution. As mentioned earlier, you can choose to use either the contract hash identifier or the alias label, depending on the specific use case. However, it is highly recommended to use the alias label right from the beginning. (if it was not immediately configured in SC deploy)

As this field will become the scope of its implementation. Every piece of state associated with the smart contract will be tracked and stored using the scope route. In other words using the label will enable the possibility to upgrade the code without loosing the connection to smart contract data.

Alias

Aliases are utilized to offer a concise and user-friendly representation of a smart contract hash identifier. They also provide the capability to link different versions of smart contracts within a single route, enabling orthogonal persistence throughout smart contract code upgrades.

Essentially, an alias is a label that points to a specific memory allocation, which hosts data and Smart Contract's Code instructions in different slots but still referenced between each other.

So Code can be upgraded by re-deploying a new version in that allocation. It is important to notice that every time a new SC is deployed in a specific allocation (triggered by the setAlias() function), the system will keep track of every encrypted block hash that was executed, represented in a registry list.

The alias must be set before making any state changes. To accomplish this, a block needs to be propagated with the appropriate embedded function specifically designed for this purpose (the setAlias() function). The Alias result has this structure: <entity>__<name> (Refer to the functions section for usage details.)

Parameters

When a Smart Contract requires inputs to delivery some special functionality, they must be declared as a strigifyable object literal.

Each smart contract can receive parameters as one object. The syntax for delimiting these attributes is achieved with the commands //INPUT and //INPUT END, as shown below:

const input =
// INPUT
{
    to: "",
    amount: 0,
};
// INPUT END

Embedded functions

The functions described below work as a two way communication channel between the instructions written in the submitted Smart Contract, the invoked Smart Contract( if is the case) and the NxtFi storage system.

By default when a block body goes through the NxtFi virtual machine, the execution logic will check if the declared scope in the functions comply the 'alias' syntax (see 'Alias'), if it doesn't this field will be automatically filled with the imported contract hash or the block hash(see SC environment variables below)

FunctionDescription

put({ name: string, value: <any> })

PUT Key/Value storage

get({ name: string})

GET Key/Value storage

gettrace({ path: string, scope?: string, options?:{object}})

GET Storage system Trace logs. allow same options <obj> as list

list({ name: string, scope?: string, options?:{maxkeys?: int, startafter?: string, raw?: boolean<default=false>})

List existing keys and directories

del({name: string})

Delete one key/dir

delMany({prefix: string, options?: <list-opt-object>})

Delete all keys that match

grantKey({name:String, pubKey:String, permissions?:Object{canGrant:Boolean, maxGrantLimit:Number, expiration: Timestamp }})

Authorize child key-pair

revokeKey({name: String})

Revoke child key-pair

log({ properties: <Computable Variables>, optional?: "key/pair" })

describe logs in block body

result({ message: <Computable variables> })

display computable results

persistArchive({ name: <fileName string-with-extension>, fileHash: <FILE_HASH>, data: <custom-metadata any-type>})

persist temporary uploaded files to permanent storage.

setAlias({scope?: String, name: String<identifier>, value: <contract-hash-pointing-to> })

configure alias name to any smart contract hash.

Smart Contract Environment available variables

To enhance the coding experience and increase the functionality of smart contracts, there are two distinct property fields that can be utilized: {block} and {contract} objects. These objects have the same structure and key names, but they refer to different blocks (if applicable).

block.<prop> is pointing to the current working block.

contract.<prop> is pointing to the invoked Smart Contract block.

For example: (current)

  • block.prevhash = hash of the last block of the scope

  • block.height = height of the current block.

  • block.data = data field of the current block.

  • block.scope = current block scope name

  • block.timeStamp = current block time-stamp

  • block.by = Signer - entity

  • block.version = API version number

  • block.signature = current block signature

  • block.hash = hash of the imported Smart Contract

For example: (invoked)

  • contract.prevhash = hash of the previews block of the invoked SC

  • contract.height = height of the SC block.

  • contract.data = data field of the SC block. (code to be executed)

  • contract.scope = SC block scope name(usually the entity who wrote the code)

  • contract.timeStamp = SC block time-stamp

  • contract.by = SC Signer

  • contract.version = API version number

  • contract.signature = SC block signature

  • contract.hash = hash of the imported Smart Contract

Example of a block declaring a smart contract:

Using different embedded functions.

// Example Smart Contract store person data

var input =
// INPUT
{
    id: "",
    name: "",
    email: "",
};
// INPUT END

put({  
    name: path/to/input.id, 
    value: input  //input object
});

grantKey({
  name: "NAME",
  pubKey: "-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwvCkzqn0vOYRzZFAlh2OeqbaEjLe/VsJUv5YWmKDnDNG2+DPQckaf6pq++Ygy8P6/0LHzLVizbYAzf48brzY3K21kwxWsCNVVl1utEXo0yHLJu9YWdfLJSjE4PUhAAxbKOZFSHwBz6kM6aNT8BSpqFL6TcUbSPCTkygmJP5Y94aRp2lZU7NBoA/QpnYpiVbOf4UaRpwMsxlWAbsqLNGxO+5Rh/mFfNHjW4mG04Sx7a2404ZCBPAIkZwJkLNOuWYH2Ez+W0VEmcF8pMbqtE6hmDDzJvtneKq+ueeXFsiZ1NmGHwfn0VCEPrUdpFIVizEoJZIz8JA9tkjZ+ZVPBfYlBQIDAQAB -----END PUBLIC KEY-----",
  permissions: {
    canGrant: true,
  }
});

const foo = get({ name: "path/to/id"});  //resolve as input object

log({ properties: "Log message", bar: foo });

del({ name: "path/to/id" });

setAlias({ name: "NAME", value: "SMART-CONTRACT-HASH" });

const idArrayList = list({
  name: "path/to/",
  scope?: "A-SCOPE",// optional, by default scope property will be populated with alias.
  options: {
    maxkeys?: int,      // optional  
    startafter?: string,   // optional  
    raw?: boolean<default=false>   // optional  
  }
});

// track list is an array of timestamps records of file storage modifications.

const trackList = gettrace({
  path: "path/to/id",
  scope: "A-SCOPE",
  options: {} // same list option obj
});

result({ message: "Smart Contract Result" + block.hash + "by =" +  block.by });



Example of a block invoking a smart contract

Invoke a smart contract using alias nomenclature.

System only accepts JSON objects as inputs.

// Entity/alias__NAME  || smart-contract-hash


{
  "id": "invoking-entity__alias",
  "name": "Input/to/trigger/functionality",
  "email": "hello@world.com",
  "pass": "store-private"
}




Last updated