_images/laptop.png

RPC Interface

The RPC interface to Uplink is an HTTP based API for which all HTTP requests methods are type POST, directed at (default) port 8545 on a server which hosts a Uplink node. There are two types of requests in this interface– the Query and the Command. Queries are not stateful and do not cause any side effects on the server other than looking up data on the ledger. Commands, however, are stateful and their purpose is to create or modify values on the ledger, sending commands directly into the P2P network; Commands issue transactions on the ledger.

The RPC protocol is configured by the rpc section in the node configuration. The port, headers and SSL configuration can be set manually:

rpc
{
  port         = 8545
  ssl          = false
  key          = "key.pem"
  crt          = "certificate.pem"
  cors-origin  = []
  cors-headers = []
}

Endpoints

Status

  • /health
  • /version
  • /peers

Blocks

  • /blocks
  • /blocks/:blockId

Accounts

  • /accounts
  • /accounts/:address

Assets

  • /assets
  • /assets/:address

Contracts

  • /contracts
  • /contracts/:address

Transactions

  • /transactions/:blockId
  • /transactions/:blockId/:transactionId

MemPool

  • /transactions/pool/size
  • /transactions/pool/all
  • /transactions/pool/all/sizes

The ‘/’ Endpoint

Several more granular interfaces have been implemented within the RPC interface in such a way that the entire meaning of the request denoting a request to a sub-interface is denoted entirely by a well-formed JSON request body sent to the / endpoint. Currently, there are three types of JSON bodies that can be sent to the RPC interface’s / endpoint:

  1. A well formed Transaction
  2. A Database Query string
  3. A Simulate message

The JSON format of these commands is simple: The JSON should contain two fields method and params, where the method denotes which sub-interface it should be dispatched to (e.g. “Transaction”, or “Simulate”), and the params define the fields of Uplink record data structures that will be decoded by the sub-interface.

Transactions

Transactions are the main data structure in Uplink denoting an atomic ledger state change and well-formed, signed transactions are able to be issued to the Uplink network through the RPC interface. JSON encoded Transactions have the form:

{
  "method" : "Transaction",
  "params" :
    {
      "header"    : <TransactionHeader>,
      "signature" : String (Base64 Sha256 hash)
      "origin"    : Address (Issuer's account)
      "timestamp" : Timestamp (Unix Timestamp, Int64)
    }
}

TODO: enumerate JSON of all transaction headers

Database Query Strings

Along side the explicit URL query endpoints, users can query the Uplink database by submitting a query string as described in the documentation page on Querying. These query strings commands must have the form:

{
  "method" : "Query",
  "params" : "query assets where holder=<Address>" # well-formed query string
}

Simulate Messages

Uplink provides a contract simulation feature, in which contracts can be simulated in a closed environment, separate from the node’s ledger state in which the simulation is created. More information about the simulation feature can be found in Simulating Contract Execution. There are three types of messages that can be sent through the RPC inteface to the Simulation process that implements the contract simulation feature:

  1. CreateSimulationMsg - to create a contract simulation
  2. UpdateSimulationMsg - to perform a stateful update on a contract simulation
  3. QuerySimulationgMsg - to query the environment of a contract simulation

Each of these messages have JSON encodings nested in the top level Simulate JSON body:

{
  "method" : "Simulate",
  "params" : <Create/Update/Query SimulationMsg JSON>
}

CreateSimulationMsg

{
  "tag"      : "CreateSimulationMsg",
  "contents" :
    {
      "issuer" : <Address>,
      "fcl"    : <String (FCL source code)>,
      "world"  : <(Optional) JSON encoding mapping of Accounts, Assets, & Contracts>
    }
}

UpdateSimulationMsg

{
  "tag"      : "UpdateSimulationMsg",
  "contents" :
    {
      "simKey"   : <String>,
      "contents" : <UpdateSimulationMsg>
    }
}
ModifyTimestamp (UpdateSimulationMsg)
{
  "tag"      : "ModifyTimestamp",
  "contents" : <ModifyTimestampMsg>
}
SetTimestamp (ModifyTimestampMsg)
{
  "tag"      : "SetTimestamp",
  "contents" : <Timestamp>
}
AddTimeDelta (ModifyTimestampMs)
{
  "tag"      : "AddTimeDelta",
  "contents" : <Timestamp>
}
CallMethod (UpdateSimulationMsg)
{
  "tag"      : "CallMethod"
  "contents" :
    {
      "caller"     : <Address>,
      "methodName" : <String>,
      "methodArgs" : [<Value>]
    }
}

QuerySimulationMsg

{
  "tag"      : "QuerySimulationMsg",
  "contents" :
    {
      "simKey"   : <String>,
      "contents" : <QuerySimulationMsg>
    }
}
QueryMethods (QuerySimulationMsg)
{
  "tag" : "QueryMethods",
}
QueryContract (QuerySimulationMsg)
{
  "tag" : "QueryContract"
}
QueryAssets (QuerySimulationMsg)
{
  "tag" : "QueryAssets"
}
QueryAsset (QuerySimulationMsg)
{
  "tag"      : "QueryAssets",
  "contents" : <Address>
}

Success Responses

All RPC requests, if successful, will respond with a JSON body of the form:

{
  "tag" : "RPCResp",
  "contents" : ...
}

when the requests have data to return, or simply:

{
  "tag" : "RPCRespOK"
  "contents" : null
}

when the requests have no data to return.

Error Responses

All RPC queries can fail with an error response. All error responses will be returned as a JSON objects unless otherwise specified and have the form:

Code: 200 - OK

Body:
{
  "tag" : "RPCRespError,
  "contents" :
    {
      "errorType" : String,
      "errorMsg" : String
    }
}

Health

Check the whether the server is alive.

URL: /health

Success Response:

Code: 200 - OK

Body: {}

Error Response:

Code: 500

Body: {}

Version

Status

Returns the server’s version of Uplink, git branch name, git commit hash, and whether or not there are unstaged commits in the deployed repo.

URL: /version

Success Response:

Code: 200 - OK

Body:
{
  "version" : String
  "branch"  : String
  "commit"  : String
  "dirty"   : String
}

Error Response:

Code: 500

Body: {}

Blocks

All Blocks

Get all blocks on the chain

URL: /blocks

Success Response:

Code: 200 - OK

Body:
  [ {
      "header" :
        {
          "origin" : String (sha256),
          "prevBlock" : String (sha256),
          "merkleRoot" : String (sha256),
          "timestamp" : Int (Unix Timestamp)
        }
      "signature" : String,
      "index" : Int,
      "transactions" : [Transaction]
    },
    ...
  ]

Block by Index

Get a specific block on the chain by specifying a block index

URL: block/:blockIdx

Success Response:

Code: 200 - OK

Body:
{
  "header" :
    {
      "origin" : String,
      "prevBlock" : String,
      "merkleRoot" : String,
      "timestamp" : Int
    }
  "signature" : String,
  "index" : Int,
  "transactions" : [Transaction]
}

Peers

All Peers

Get a list of all peers (hostname:port) in the network

URL: /peers

Success Response:

Code: 200 - OK

Body: [ String, String, ... ]

Accounts

All Accounts

Get all accounts in the network

URL: /accounts

Success Response:

Code: 200 - OK

Body:
[ {
    "publicKey" : String,
    "address" : String,
    "timezone" : String,
    "metadata" :
      {
        String : String,
        String : String,
        ...
      }
  },
  ...
]

Account by Address

Get a specific account by providing an address

URL: /accounts/:address

Success Response:

Code: 200 - OK

Body:
{
  "publicKey" : String,
  "address" : String (sha256),
  "timezone" : String,
  "metadata" :
    {
      String : String,
      String : String,
      ...
    }
}

Assets

All Assets

Get all assets in the network

URL: /assets

Success Response:

Code: 200 - OK

Body:
[
  {
    "asset": {
      "issuedOn": Int (Unix Timestamp),,
      "assetType": {
        "type": String,
        "precision": Int
      },
      "name": String,
      "reference":  String || null,
      "supply": Int,
      "holdings": {
        String (Address) : Int,
        String (Address) : Int,
        ...
      },
      "issuer":  String (Address),
    },
    "address": String (Address)
  },
  ...
]

Asset by Address

Get a specific asset in the network by providing the address of the asset

URL: /assets/:address

Success Response:

Code: 200 - OK

Body:
{
  "asset": {
    "issuedOn": Int (Unix Timestamp),
    "assetType": {
      "type": String,
      "precision": Int
    },
    "name": String,
    "reference":  String || null,
    "supply": Int,
    "holdings": {
      String (Address) : Int,
      String (Address) : Int,
      ...
    },
    "issuer":  String (Address),
  },
  "address": String (Address)
}

Contracts

All Contracts

Get all contracts on the ledger

URL: /contracts

Success Response:

Code: 200 - OK

Body:
[
  {
    "contract": {
      "timestamp": Int,
      "storage": {
        String : String
        String : String
        ...
      },
      "methods": [string,...],
      "script": String (code)
    },
    "address": String (Address)
  }
  ...
]

Contract by Address

Get a specific contract by providing the address of the contract

URL: /contracts/:address

Success Response:

Code: 200 - OK

Body:
{
  "contract": {
    "timestamp": Int,
    "storage": {
      String : String
      String : String
      ...
    },
    "methods": [string,...],
    "script": String (code)
  },
  "address": String (Address)
}

Transactions

All Transactions in Block

Get the list of transactions in a block by providing the block index

URL: /transactions/:blockIdx

Success Response:

Code: 200 - OK

Body:
[ {
    "header" : TransactionHeader,
    "signature" : String (Base64 Sha256 hash)
    "from" : String (Address),
    "fromPub" : String (ECDSA Public key fingerprint),
    "timestamp" : Timestamp (Unix Timestamp, Int64)
  },
  ...
]

Transaction by Block Index and Transaction Index

Get a specific transaction by proving the block index and transaction index of the transaction

URL: /transactions/:blockIdx/:transactionIdx

Success Response:

Code: 200 - OK

Body:
{
  "header" : TransactionHeader,
  "signature" : String (Base64 Sha256 hash)
  "from" : String (Address),
  "fromPub" : String (ECDSA Public key fingerprint),
  "timestamp" : Timestamp (Unix Timestamp, Int64)
}

JSON Values

Call transactions require the value types of arguments to the method being called as they are fields in the transaction header, and transaction headers must be signed with ECDSA. The values must be binary serialized in the specific format that an Uplink node expects such that the uplink node can validate the ECDSA signature provided by the SDK the transaction issuer is using.

VInt Int64             -- ^ Integral types
VFloat Double          -- ^ Floating types
VBool Bool             -- ^ Boolean value
VAddress Address       -- ^ Address
VAccount Address       -- ^ Account Address
VAsset Address         -- ^ Asset Address
VContract Address      -- ^ Contract Address
VMsg SafeString        -- ^ Msgs (ASCII)
VSig (SafeInt,SafeInt) -- ^ ESDSA Sig
VVoid                  -- ^ Void
VDateTime DateTime     -- ^ DateTime value
VTimeDelta TimeDelta   -- ^ TimeDelta value
VState Label           -- ^ Named state label

where
  SafeInt              -- Int value not exceeding 512 bytes
  SafeString           -- String value not exceeding 10 kilobytes

Encoding:

All FCL values are encoded as a sequence of big-endian bytestrings. The format of all serialized bytestrings is a single signed-char byte denoting the constructor of the value, and then the serialized raw value/s that make up the components of the value.

(VInt n) - 9 bytes

<unsigned char>      : 0       [1 byte]
<unsigned long long> : n       [8 bytes]
(VFloat f) - 9 bytes

<unsigned char>      : 2       [1 byte]
<double>             : f       [8 bytes]
(VBool b) - 2 bytes

<unsigned char>      : 4       [1 byte]
<_Bool (C99)>        : b       [1 byte]
(VAddress a) - 33 bytes

<unsigned char>      : 5       [1 byte]
<char[]>             : a       [32 bytes]
(VAccount a) - 33 bytes

<unsigned char>      : 6       [1 byte]
<char[]>             : a       [32 bytes]
(VAsset a) - 33 bytes

<unsigned char>      : 7       [1 byte]
<char[]>             : a       [32 bytes]
(VContract c) - 33 bytes

<unsigned char>      : 8       [1 byte]
<char[]>             : c       [32 bytes]
(VMsg (SafeString s)) - [3 - 100003 bytes]

<unsigned char>      : 9       [1 byte]
<unsigned short> : len(s)  [2 bytes]
<char[]>             : s       [len(s) bytes]
(VSig (SafeInt r, SafeInt s)) - [1029 bytes]

<unsigned char>      : 10                 [1 byte]
-- Encoding of 'r'
<unsigned char>      : signOf(r)          [1 byte]
<unsigned char>      : numBytes(s)        [2 byte]
-- Big Endian encoding of 's', numBytes(n) bytes (maximum 512)
<unsigned char>      : byteNminus1        [1 byte]
...
<unsigned char>      : byte1              [1 byte]
<unsigned char>      : byte0              [1 byte]

-- Encoding of 's'
<unsigned char>      : signOf(s)          [1 byte]
<unsigned char>      : numBytes(s)        [2 byte]
-- Big Endian encoding of 's', numBytes(n) bytes (maximum 512)
<unsigned char>      : byteNminus1        [1 byte]
...
<unsigned char>      : byte1              [1 byte]
<unsigned char>      : byte0              [1 byte]
(VVoid) - 1 byte

<unsigned char>      : 11      [1 byte]
(VDateTime (DateTime y mo d h m s z w)) - 513 bytes

<unsigned char>      : 12      [1 byte]
<unsigned long long> : y       [8 bytes]
<unsigned long long> : mo      [8 bytes]
<unsigned long long> : d       [8 bytes]
<unsigned long long> : h       [8 bytes]
<unsigned long long> : m       [8 bytes]
<unsigned long long> : s       [8 bytes]
<unsigned long long> : z       [8 bytes]
<unsigned long long> : w       [8 bytes]
(VTimeDelta (TimeDelta (Period y mo d) (Duration h m s ns)) - 449 bytes

<unsigned char>      : 13      [1 byte]
<unsigned long long> : y       [8 bytes]
<unsigned long long> : mo      [8 bytes]
<unsigned long long> : d       [8 bytes]
<unsigned long long> : h       [8 bytes]
<unsigned long long> : m       [8 bytes]
<unsigned long long> : s       [8 bytes]
<unsigned long long> : ns      [8 bytes]
(VUndefined) - 1 byte

<unsigned char>      : 15      [1 byte]

Transport Level Security

To run the RPC server using SSL, first generate a certificate. In a production environment use a certificate authorized by an authority and managed by an enterprise identity management system. For testing a self-signed certificate can be crated by invoking the following OpenSSL commands:

$ openssl genrsa -out key.pem 2048
$ openssl req -new -key key.pem -out certificate.csr
$ openssl x509 -req -in certificate.csr -signkey key.pem -out certificate.pem

And then update your node.config with the paths to the certificate and key and set ssl to true.

rpc {
    port         = 8545
    ssl          = true
    key          = "key.pem"
    crt          = "certificate.pem"
    cors-origin  = []
    cors-headers = []
}