TSS Node callback mechanism

After the successful setup of the TSS Node, it will automatically establish a connection with Cobo Portal and await tasks sent by the Cobo Portal backend. Currently, there are three task types: key generation (KeyGen), transaction signing (KeySign), and soft key recovery (KeyReshare).

In cases where the callback mechanism is not configured, the TSS Node promptly executes each task upon receipt from the Cobo Portal backend. If the callback mechanism is enabled, the TSS Node sends an approval request to the callback server upon task reception. Task execution occurs only upon approval from the callback server.

The communication between the TSS Node and the callback server utilizes the HTTP protocol, and Cobo Portal implements JSON Web Token (JWT) signed with the RS256 algorithm. For more information on JWT, please click here.

  • During TSS Node initialization, both the TSS Node and the callback server generate and configure an RSA private/public key pair.
  • Upon task reception, the TSS Node constructs a CallbackRequest and generates a JWT token using its RSA private key. This request is then sent to the callback server via the HTTP POST method.
  • Upon receipt, the callback server validates the JWT signature using the TSS Node’s RSA public key. It constructs a CallbackRequest and signs it using its RSA private key before sending the JWT back to the TSS Node.
  • Upon receiving the HTTP response, the TSS Node validates the JWT signature using the callback server’s RSA public key.
  • Once validated, the TSS Node executes or rejects the task based on the response received from the callback server.

Callback server requirements

HTTP API

The callback server operates as an HTTP server with the following supported endpoints:

  • Path: /v1/check
  • Method: POST
  • Body: Encoded with x-www-form-urlencoded

HTTP request parsing

The callback server will send HTTP Request in the following manner:

  • Generate CallbackRequest.
  • Serialize CallbackRequest to derive CallbackRequestJsonString.
  • Utilize CallbackRequestJsonString as the JWT payload and sign it using its RSA private key to create a JWT.
  • Submit the HTTP POST request via an HTML form with TSS_JWT_MSG as the key and JWT as the value.

The callback server will process the HTTP Request in the following manner:

  • Retrieve the data in the HTTP POST request using TSS_JWT_MSG as the key and the JWT as the value.
  • Use the TSS Node’s RSA public key to validate the signed JWT.
  • Retrieve the payload in the JWT and deserialize it using CallbackRequest.
  • Deserialize the meta field to retrieve the information in the request.

Risk controls

For each CallbackRequest, it is essential to conduct a risk control assessment in accordance with your organization’s security requirements. This step is crucial to ensure the validation of the request.

HTTP responses

The callback server will structure and send the HTTP response in the following manner:

  • Generate CallbackResponse.
  • Serialize CallbackResponse to derive CallbackResponseJsonString.
  • Utilize CallbackResponseJsonString as the JWT payload and sign it using its RSA private key to create a JWT.
  • Use the JWT as the HTTP response and return it to the TSS Node.

CallbackRequest descriptions

ParameterTypeDescription
request_idstringUnique ID of the callback request.
request_typeintThe types of callback requests are enumerated as follows:
TypePing: 0
TypeKeyGen: 1
TypeKeySign: 2
TypeKeyReshare: 3
request_detailstringThe specifics of the callback request. Each type of callback request includes a distinct request_detail structure. The format for this information is a serialized JSON string.
extra_infostringAdditional information associated with the callback request. Each type of callback request includes a unique extra_info structure. The format for this information is a serialized JSON string.

When request_type is equal to TypeKeyGen, the structure of request_detail is as follows:

ParameterTypeDescription
thresholdintSignature threshold.
node_ids[]stringNode IDs associated with the generation of all private key shares under the MPC Wallet.
curveintSignature algorithm that has been used:
0: SECP256K1
2: ED25519
task_idstringTask ID

The structure of extra_info is as follow:

ParameterTypeDescription
cobo_idstringUnique ID of the key generation (KeyGen) request.
api_request_idstringRequest ID either passed in by the client using APIs or, if the request is not sent via APIs, Cobo will automatically generate a Request ID.

When request_type is equal to TypeKeySign, the structure of request_detail is as follows:

ParameterTypeDescription
group_idstringGroup ID used during transaction signing.
root_pub_keystringRoot extended public key.
used_node_ids[]stringTSS Node IDs used during transaction signing.
bip32_path_list[]stringAddress paths (BIP32).
msg_hash_list[]stringHashes to be signed.
tweak_list[]stringTweaks to be signed.
signature_typeint32Signature algorithm that has been used:
1: ECDSA
2: EdDSA
3: Schnorr
tss_protocolint32TSS signature protocol that has been used:
0: Unknown
1: GG18
2: Lindell
3: EDDSATss
task_idstringTask ID.

The structure of extra_info is as follow:

ParameterTypeDescription
cobo_idstringUnique ID of the transaction signing (KeySign) request.
api_request_idstringRequest ID of the withdrawal request. If the request is initialized via API, the value should be the same as request_id. If the request is initialized via Cobo Portal, Cobo will automatically generate a Request ID.
transaction_typeTransactionTypeEnumPlease refer to TransactionTypeEnum.
operationTransactionOperationEnumPlease refer to TransactionOperationEnum.
coinstringAsset name.
decimalintDecimal precision.
from_addressstringThe address from which assets are withdrawn.
amountstring(Optional) This transaction amount may contain decimal places (e.g., 1 BTC is divisible to eight decimal places, and 100,000,000 represents 1 BTC).
to_addressstringDeposit address.
to_address_detailsjson(Optional) A list of deposit addresses, applicable to the UTXO model. The value must adhere to the JSON structure of list[ToAddressDetail].
feeint(Optional) For BTC transactions, this field returns the transaction fees per byte, with the unit being satoshi.
gas_priceint(Optional) For ETH transactions, this field returns gas fees, with the unit being gwei.
gas_limitint(Optional) For ETH transactions, this field returns the gas limits.
extra_parametersjsonAdditional parameters for the transaction. Please refer to the description of create_transaction.
replace_cobo_idstring(Optional) ID of a Cobo transaction that has been designated as Replace-By-Fee (RBF).
api_keystring(Optional) API_KEY of the withdrawal request initialized through APIs.
spenderstring(Optional) Email information of the withdrawer. This is applicable when the withdrawal is initiated through Cobo Portal.
raw_tx_hexstringTransaction hash.
custody_wallet_namestringWallet name.
gas_station_child_idstringCobo ID for gas station transactions.
notestringTransaction remarks.
raw_tx_infojsonRaw information necessary for constructing the transaction to be signed.

The structure of ToAddressDetail is as follows:

ParameterTypeDescription
to_addressstringWithdrawal address.
amountstringThis transaction amount may contain decimal places (e.g., 1 BTC is divisible to eight decimal places, and 100,000,000 represents 1 BTC).

The structure of extra_parameters is as follows:

ParameterTypeDescription
inputs_to_spendlist[Input]Unspent Transaction Output (UTXO) to spend. This applies to UTXO-type blockchains, such as Bitcoin.
inputs_to_excludelist[Input]Unspent Transaction Output (UTXO) to exclude from spending. This applies to UTXO-type blockchains, such as Bitcoin.

The structure of input is as follows:

ParameterTypeDescription
tx_hashstringTransaction hash.
vout_nintTransaction index or number.

The structure of TransactionTypeEnum is as follows:

ValueNameDescription
100TRANSACTION_FROM_WEBTransaction initiated via Cobo Portal.
102TRANSACTION_FROM_APITransaction initiated via APIs.
103TRANSACTION_RBF_API_SPEEDUPThe transaction to speed up, which was initiated via API.
104TRANSACTION_RBF_WEB_SPEEDUPThe transaction to speed up, which was initiated via Cobo Portal.
105TRANSACTION_RBF_API_DROPThe transaction to cancel, which was initiated via API.
106TRANSACTION_RBF_WEB_DROPThe transaction to cancel, which was initiated via Cobo Portal.

The structure of TransactionOperationEnum is as follows:

ValueNameDescription
100TRANSFERTransfer.
200CONTRACT_CALLSmart contract calls.

When request_type is equal to TypeKeyReshare, the structure of request_detail is as follows:

ParameterTypeDescription
old_group_idstringOld group ID.
root_pub_keystringRoot extended public key.
curveintSignature algorithm that has been used:
0: SECP256K1
2: ED25519
used_node_ids[]stringSelected Node IDs from the previous group that have been used for soft key recovery.
old_thresholdintOld signature threshold.
new_thresholdintNew signature Threshold.
new_node_ids[]stringNew TSS Node IDs associated with the generation of all private key shares under the MPC Wallet.
task_idstringTask ID.

The structure of extra_info is as follows:

ParameterTypeDescription
cobo_idstringUnique ID of the soft key recovery (KeyReshare) request.
api_request_idstringRequest ID either passed in by the client using APIs or, if the request is not sent via APIs, Cobo will automatically generate a Request ID.

CallbackResponse descriptions

ParameterTypeDescription
statusintIf the status is 0, it indicates that the callback server is normal. Other values suggest that the callback server has encountered an error.
request_idstringRequest ID of the callback. The value must be the same as the request_id in CallbackRequest.
actionstringInstructions for task execution, represented as an enum with two possible values:
APPROVE: The task should be executed.
REJECT: The task should be rejected, and the reason can be retrieved from the error field.
errorstringIf the returned status is not 0, it indicates an error within the callback server. If the returned status is 0 and the action returns a “REJECT,” it provides the reason for task rejection.

Callback server examples

Cobo Portal offers callback server examples in various programming languages. You can refer to these examples to swiftly deploy a callback server. Alternatively, JWT code libraries are available for multiple programming languages. You can refer to JWT’s official website to deploy a callback server.

In the event that the TSS Node fails to receive the HTTP response, it will continue to send requests to the callback server. In these instances, when the maximum number of retries is reached, the response will be categorized as “REJECT.”

Callback server configuration

On the server where the callback server is deployed, you’ll need to complete the following configurations.

  1. Generate the callback server’s RSA private key.
openssl genrsa -out callback-server-pri.pem 4096
  1. Export the callback server’s RSA public key.
openssl rsa -in callback-server-pri.pem -pubout >callback-server-pub.key
  1. Copy the exported RSA’s public key (i.e. the callback-server-pub.key file) to the server where the TSS Node is deployed.

  2. Copy the RSA’s public key of the TSS Node to the config files of the callback server.

    • If you are using the callback server codes provided by Cobo Portal, refer to the configuration instructions provided in the samples.
  3. After successfully completing TSS Node initialization, use the following command to query the RSA’s public key of the TSS Node:

sudo ./tss-node.sh info

The output example is as follows.

INFO[2022-12-14T07:09:44Z] Callback public key:

-----BEGIN PUBLIC KEY-----

MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4ki4fHY4/oSJOYBxBUI1

GUdLCUF7wMFYlfkpI/bSf7fGvzaygtZdORG6OR4vAoKxvqK/ZbJITMR5rS7pNxOv

vpLbsdVfJZGQn6wW8wLEx9ZB18JXt99oTESaxzoxMteXBC6tgYqWhSS386LfjSxQ

a4EvPunTRdYrO9VmnDWHM06/c76UxcS038pJ4OByXx7sH0xzS4MZQhZWyfYkqBT2

Ym9g46VXQ+CVZyWYXKPMyGPVTR1979lfJ1bFpYADNU3YUSK1mq0xaH9lQORFBs6/

0kK8m27BGR7h59rFJGgeZ2SNGzcEmiAp7rjOtFpLj1OmNXtjGhTxpn8omBrdH86+

qZfKsIm+O84e9S7uPMnx9hIpJHTcPoseW0d+WBoNplJQg22LUswPwaVgSHMB2U1B

aFkEzMAkRmGIejlMG25uYaVlA0Mf9E+/JaZSGPVuq43lhHCE08pNxGrak6/vTPTv

jAM449yTHrjDCDrFk/LdHqk5Ipt0b9CeHbCsQSs4BxHlDp2GEBJn1OEiizDwScWb

YiFi3a6PAvqDO56KEUFMXpC8CzgJ8svmQoldIAWSKGpdnuxUMcD+glCsi7K7+FI+

HDepO/4ejCm9DzuTUHG4PE0Ymb3PV8cZbLlGSZD/yG1541/xlHmzlOpAa8OdZBfb

s8wADQvpbJTyPkkMyq/i5SkCAwEAAQ==

-----END PUBLIC KEY----- 

TSS Node configuration & callback server startup

TSS Node configuration

On the server where the callback server is deployed, you’ll need to complete the following configurations.

  1. Copy the callback server’s RSA public key file to the configs directory of the TSS Node.
cobo-tss-node-generic

├── configs

│ ├── callback-server-pub.key

│ └── cobo-tss-node-config.yaml
  1. Modify the cobo-tss-node-config.yaml file.

    • cb_server

      • service_address indicates the callback server address.
      • pubkey_path indicates the file path of the callback server’s RSA.
    • token_expire_minutes indicates the timeout period of the JWT (unit: minutes).

    • retry_time indicates the number of retries if the callback request fails.

    • sleep_seconds indicates the time interval between each retry (unit: minutes).

You can configure multiple callback servers. The following output uses two callback servers as an example:

callback:

cb_server:

- service_address: http://callback-server-01:8080/v1/check

pubkey_path: configs/callback-server-pub-01.key

- service_address: http://callback-server-02:8080/v1/check

pubkey_path: configs/callback-server-pub-02.key

token_expire_minutes: 2

retry_times: 60

sleep_seconds: 60

After completing the aforementioned configurations, you can initiate the startup of both the callback server and the TSS Node.