Skip to content

Substrate Signing Request Specification

Nikolaus Heger edited this page Jan 27, 2023 · 8 revisions

Substrate Signing Request Standard and Spec

Version

0.1

Overview

A Substrate Signing Request is a request to sign a substrate transaction. A wallet implementing this standard can create SSR URLs and process SSR URLs.

A substrate transaction is encoded as JSON.

This allows a signing wallet to sign arbitrary transactions.

Properties

A SSR has the following properties:

  • Chain ID - a substrate Chain ID (insert link) specifying the chain the transaction should be signed on
  • Type - identity or sign - The default type is sign. Standard 1.0 will have an identity type request that allows a user to authenticate themselves using their substrate account key.
  • Callback URL - called for identity and also to inform a backend that a transaction has been made or has failed.
  • Transactions - a list of extrinsics calls with data parameters to be executed.
  • "signer" account template - allows the SSR to specify the "signer" account id without knowing this account ID - the client replaces all occurrences of signer with the signing account they choose to execute the transction.
  • SSR is a JSON object, encoded as URL with a ssr:// prefix
  • ssr URL can be encoded as QR code for convenient parsing with a wallet

Version 0.1

Version 0.1 implements the standard to a minimal degree, it has the following limitations

  • No identity request, only transactions
  • Only a single extrinsics call, not more than 1

It implements the rest of the standard.

JSON format sign and send request

Sample

{
  "chainId": "hashed",
  "callback": "https://myserver.somwhere.com/?tx_id=$tx_id"
  "transactions": [
    {
      "module": "recovery",
      "call": "createRecovery",
      "sender": "signer",
      "params": "[[\"5Da6BeYLC3BRvS2H3bQ6JWgMGZtqKGdaoKMPhdtYMf56VaCU\",\"5EUqh98iKNwWQjpzYQPVw3LEQiiaVMaB4Yp2ugXA5fMKFDLk\",\"5GEbpz29EkSM3vKtzuUEXtwpK8vguYm2TRRsmekQufYJDJpz\"],2,3000]"
    }
  ]
}

Corresponding encoded URL

ssr://H4sIAAAAAAAAE0WMu26DQBRE_-XWFBYEZLsLhpDwiAwO2AQormF5hKeXBRQs_7tXSpFudObM3CEtseo-MthDiWNJMhCAUexGTFnVdyPsozu0fTY1hCuUpP1M6C-XUmwaTlJKkBHvn4-kywjlzVgVHQ8CDEix5UcQRTHIGioqCe2DpHrzSXyXrq5ingvH-GY3y8iwt5xjmbHQyWUlwIMfg8BHun8rd9vK-lzO7s-whu4xWCRbd6sKAwfVl3AQp-LyKueO9abZ9d_I0K_DKu70-uRIs8XWydcvbBms7VxMYSt-ed7Yktqd8tDUzGGNIRFEQdpsNgk8kscTHuDEbRoBAAA=

Identity Request (draft)

A server generates an identity request to identify an account

The server includes a challenge which the signer has to sign and send back

The callback link includes placeholders for the challenge, the account used for signing, and the signed challenge.

Since only the account holder has the private key, only the account holder can sign the challenge for the account, thereby priving identity to the server.

{
  "type": "identity",
  "challenge": "afx90972"
  "callback": "https://myserver.somwhere.com/?challenge=$c&sign=$s&acct=$acct"
}

Encoding and decoding

  1. Take a json Object - either a dictionary / map style object, or an array.
  2. Convert it to a string using a JSON encoder
  3. Convert the string to bytes with UTF8 encoding.
  4. Compress the bytes with gzip
  5. Convert the zipped bytes to a string using the Base64Url standard - a subset of base64 suitable for URLs.
  6. Create URL with URL scheme ssr://

For decoding, the reverse.

Code example (dart)

  static const urlScheme = "ssr";

  String jsonToSigningRequestUrl(dynamic jsonObj) {
    final string = json.encode(jsonObj);
    final bytes = utf8.encode(string);
    final zippedBytes = gzip.encode(bytes);
    final base64url = base64Url.encode(zippedBytes);
    return "$urlScheme://$base64url";
  }

  /// returns JSON object
  dynamic signingRequestUrlToJson(String url) {
    final urlComponents = url.split("://");
    if (urlComponents.length == 2) {
      if (urlComponents[0] == urlScheme) {
        final payload = urlComponents[1];
        final zippedBytes = base64Url.decode(payload);
        final bytes = gzip.decode(zippedBytes);
        final string = utf8.decode(bytes);
        final result = json.decode(string);
        return result;
      } else {
        throw "Unsupported URL scheme: ${urlComponents[0]}";
      }
    } else {
      throw "Invalid URL: $url";
    }
  }

More efficient encoding methods

A full binary encoding would yield the perfect compressed data.

However, experiments show that binary is only around 30% more efficient than zipped json (Reference Link)

Binary encoding of all possible field values would significantly increase the complexity of implementing clients that can handle this format.

Whereas JSON, UTF8, and gzip are all readily available on all platforms, and easy and straightforward to test and to use.

QR codes are limited in size, so size is important.

However, the benefits of simplicity outweigh the drawbacks of a roughtly 30% larger payload.

Clone this wiki locally