Skip to content

WebSockets Subscription Guide

Deng Lun edited this page Mar 9, 2020 · 2 revisions

QuarkChain WebSockets

QuarkChain WebSockets are managed by slaves in a cluster. They have similar interfaces with Ethereum with difference of full shard id in all APIs.

Enable WebSocket Services

Make sure the slaves are start up with --ws option to enable WebSocket services.

The default ws port is 38590 but you need to specify different ports for each slave using --ws_port option.

By default the cluster will only accept WebSocket connections from localhost. You can change this to accept connections from anyone with --rpcaddr 0.0.0.0 if necessary, but please be noted this is **NOT **advisable from a security standpoint.

For example,

./cluster --cluster_config $CONFIG --ws --ws_port 38590 --ws_host 0.0.0.0 --service S0 > ./test/S0.log 2>&1 & 
./cluster --cluster_config $CONFIG --ws --ws_port 38591 --ws_host 0.0.0.0 --service S1 > ./test/S1.log 2>&1 & 
./cluster --cluster_config $CONFIG --ws --ws_port 38592 --ws_host 0.0.0.0 --service S2 > ./test/S2.log 2>&1 & 
./cluster --cluster_config $CONFIG --ws --ws_port 38593 --ws_host 0.0.0.0 --service S3 > ./test/S3.log 2>&1 &

NOTE in pyquarkchain you specify ws port with WEBSOCKET_JSON_RPC_PORT in SLAVE_LIST section in cluster configuration file and enable WebSocket service at the same time, but this is not the case in goquarkchain.

To check if the WebSocket endpoint is opened, find the following message in slave service log:

INFO [02-25|15:57:01.785] WebSocket endpoint opened                url=ws://127.0.0.1:38591

Create Subscriptions

Subscriptions are creates with a regular RPC call with subscribe as method. The first parameter is subscription name, and the second is full shard id in hex with '0x' prefix. If successful it returns the subscription id. The following javascript sample subscribes newHeads on shard 0x70001.

    let ws = new WebSocket("ws://127.0.0.1:38593");
    ws.onopen = function() {
      ws.send(
        JSON.stringify({
          "jsonrpc": "2.0",
          "method": "subscribe",
          "params": ["newHeads","0x70001"],
          "id": 0
        })
      );
    };

Result:

{"jsonrpc":"2.0","id":0,"result":"0xe0840414c530d72e5c2f1fe64f6311cc"}

You can find which slave manage which shard(branch) in the master log when it starts up. For example:

INFO [02-13|10:35:59.460] slave connection manager                 branch:=1 is run by slave=S0
INFO [02-13|10:35:59.460] slave connection manager                 branch:=262145 is run by slave=S0
INFO [02-13|10:35:59.462] slave connection manager                 branch:=65537  is run by slave=S1
INFO [02-13|10:35:59.462] slave connection manager                 branch:=327681 is run by slave=S1
INFO [02-13|10:35:59.463] slave connection manager                 branch:=131073 is run by slave=S2
INFO [02-13|10:35:59.463] slave connection manager                 branch:=393217 is run by slave=S2
INFO [02-13|10:35:59.464] slave connection manager                 branch:=458753 is run by slave=S3
INFO [02-13|10:35:59.464] slave connection manager                 branch:=196609 is run by slave=S3

Since the ws parameter requires the full shard id given in hexadecimal string, this table is for your convenience:

DEC   : HEX
---------------
     1: 0x1
458753: 0x70001
 65537: 0x10001
131073: 0x20001
196609: 0x30001
262145: 0x40001
327681: 0x50001
393217: 0x60001 

You can then listen to the event after subscription:

    ws.onmessage = function(msg) {
      console.log(msg.data)
    };

Supported subscriptions

newHeads

Fires a notification each time a new header is appended to the shard chain, including chain reorganizations. Users can use the bloom filter to determine if the block contains logs that are interested to them.

In case of a chain reorganization the subscription will emit all new headers for the new chain. Therefore the subscription can emit multiple headers on the same height.

Subscribe parameters

["newHeads", fullShardId]

Example:

{
    "jsonrpc": "2.0", 
    "id": 1, 
    "method": "subscribe", 
    "params": ["newHeads", "0x1"]
}

Message:

{
  "jsonrpc": "2.0",
  "method": "_subscription",
  "params": {
    "subscription": "0xe0840414c530d72e5c2f1fe64f6311cc",
    "result": {
      "chainId": "0x0",
      "coinbase": [
        {
          "balance": "0x2d1a51c7e0050000",
          "tokenId": "0x8bb0",
          "tokenStr": "QKC"
        }
      ],
      "difficulty": "0xa4f954",
      "extraData": "0x",
      "fullShardId": "0x1",
      "gasLimit": "0xb71b00",
      "hash": "0xbf0a91022b69442dfc49e676e46953bc2f2d0bee933a93dff4cd21e6bf147920",
      "hashPrevMinorBlock": "0xcfeaf8441a601f7963c00f15b5981baa4ea525e771616e6e8f342ca16016890e",
      "hashPrevRootBlock": "0xf3f8270972fec7a55d0c02cb8f59a3c4f4f8f834b95304b7bde640b0b5c75737",
      "height": "0x29",
      "id": "0xbf0a91022b69442dfc49e676e46953bc2f2d0bee933a93dff4cd21e6bf14792000000001",
      "idPrevMinorBlock": "0xcfeaf8441a601f7963c00f15b5981baa4ea525e771616e6e8f342ca16016890e00000001",
      "miner": "0x000000000000000000000000000000000000001100000000",
      "nonce": "0x16097a05affc3661",
      "shardId": "0x0",
      "timestamp": "0x5e4d0d20"
    }
  }
}

logs

Returns logs that are included in new imported blocks and match the given filter criteria.

In case of a chain reorganization previous sent logs that are on the old chain will be resend with the removed property set to true. Logs from transactions that ended up in the new chain are emitted. Therefore a subscription can emit logs for the same transaction multiple times.

Subscribe parameters

["logs", fullShardId, filter]

filter is an object with the following (optional) fields:

address, either an address or an array of addresses. Only logs that are created from these addresses are returned (optional)

topics, only logs which match the specified topics (optional)

Example:

{
  "jsonrpc": "2.0",
  "method": "subscribe",
  "params": [
    "logs",
    "0x1",
    {
      "address": "0xed21b4d02ce3d799f2543334056744008bdf83a2",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
      ]
    }
  ],
  "id": 0
}

Message:

{
  "jsonrpc": "2.0",
  "method": "_subscription",
  "params": {
    "subscription": "0x3da3940847e40936077218cf1ceb0d55",
    "result": {
      "address": "0xed21b4d02ce3d799f2543334056744008bdf83a2",
      "blockHash": "0xa8f308832b9655f586901014d87085fa94ff30b4e24659938acc0b4ef6df299b",
      "blockHeight": "0xafc",
      "blockNumber": "0xafc",
      "data": "0x000000000000000000000000000000000000000000000000000000000000000a",
      "logIndex": "0x0",
      "recipient": "0xed21b4d02ce3d799f2543334056744008bdf83a2",
      "removed": false,
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000b067ac9ebeeecb10bbcd1088317959d58d1e38f6",
        "0x000000000000000000000000fee3d08216f0bfe96a5c0f5df9ec9b2577ad64f0"
      ],
      "transactionHash": "0xb009213cab0055c7305847760cdecd6ce99bf2b30a41ba26aadc3ac8ab3de50d",
      "transactionIndex": "0x0"
    }
  }
}

newPendingTransactions

Returns the hash for all transactions of the shard that are added to the pending state and are signed with a key that is available in the node.

Subscribe parameters

["newPendingTransactions", fullShardId]

Example:

{
  "jsonrpc": "2.0",
  "method": "subscribe",
  "params": [
    "newPendingTransactions",
    "0x1"
  ],
  "id": 0
}

Message:

{
  "jsonrpc": "2.0",
  "method": "_subscription",
  "params": {
    "subscription": "0xa0688361d65fb52d720bf92d1ae00428",
    "result": {
      "blockHeight": "0x0",
      "blockId": "0x0ce4d51d78f9d944a462f9dc739fb339ef6be41f5e1b4e940eeb35880f00d0ce00000001",
      "chainId": "0x0",
      "data": "0xa9059cbb000000000000000000000000fee3d08216f0bfe96a5c0f5df9ec9b2577ad64f0000000000000000000000000000000000000000000000000000000000000000a",
      "from": "0xb067ac9ebeeecb10bbcd1088317959d58d1e38f6",
      "fromFullShardKey": "0x00000000",
      "fullShardId": "0x1",
      "gas": "0x1e8480",
      "gasPrice": "0x2540be400",
      "gasTokenId": "0x8bb0",
      "gasTokenStr": "QKC",
      "hash": "0x325849ccfde360de3851e1335094b09db1ab26c2d06e14bc8deaf54e729a7b39",
      "id": "0x325849ccfde360de3851e1335094b09db1ab26c2d06e14bc8deaf54e729a7b3900000000",
      "networkId": "0xfc",
      "nonce": "0x9",
      "r": "0x945ecdebe31fdc77ec93c3d6793b8b90f6cbc4cb25f96821b354b4f187fcffbc",
      "s": "0x3afcfea2636cba43c38bb77f4b49684c5e6a4cf42fff4b31dd2519a0f86b1ab1",
      "shardId": "0x0",
      "timestamp": "0x0",
      "to": "0xed21b4d02ce3d799f2543334056744008bdf83a2",
      "toFullShardKey": "0x00000000",
      "transactionIndex": "0x0",
      "transferTokenId": "0x8bb0",
      "transferTokenStr": "QKC",
      "v": "0x1c",
      "value": "0x0"
    }
  }
}

syncing

Indicates when the slave node starts or stops synchronizing this shard. The result can either be a boolean indicating that the synchronization has started (true), finished (false) or an object with various progress indicators.

Subscribe parameters

["syncing", fullShardId]

Example:

{
  "jsonrpc": "2.0",
  "method": "subscribe",
  "params": [
    "syncing",
    "0x1"
  ],
  "id": 0
}

Message:

{
  "jsonrpc": "2.0",
  "method": "_subscription",
  "params": {
    "subscription": "0x9c9d9343e92ba09dd9d52dfd79b4d76",
    "result": {
      "syncing": false,
      "status": {
        "currentBlock": 11,
        "highestBlock": 12
      }
    }
  }
}