Create a new vault with specified parties. This does not join a key generation yet. Only creates it.
A vault can never be updated to have new signers, a vault is always considered valid, if participants change, we consider that a new vault, as the old vault can technically still be used to sign.
Request:
{
"name": "My Vault",
"hex_chain_code": "80871c0f885f953e5206e461630a9222148797e66276a83224c7b9b0f75b3ec0",
"parties": [
"party_1_id",
"party_2_id",
"Vultisigner"
]
}
Response (initial):
{
"vault_id": "unique_vault_id",
"name": "My Vault",
"hexChainCode": "80871c0f885f953e5206e461630a9222148797e66276a83224c7b9b0f75b3ec0",
"parties": [
"party_1_id",
"party_2_id",
"Vultisigner"
],
"created_at": "timestamp"
}
After the vault is created, you can call this endpoint, this will start the key generation process, spinning up a background job (using cmd/worker
with asynq
) to join relay keygen session.
This endpoint is meant to return the session_id, which allows the other parties to join the keygen session.
Request:
// nothing
Response:
{
"vault_id": "unique_vault_id",
"session_id": "unique_session_id",
}
You can retrieve the status of a keygen session here
Response:
{
"vault_id": "unique_vault_id",
"session_id": "unique_session_id",
"status": "pending", // or "in_progress", "completed", "failed", "prime-generating"
}
Response (after creation):
{
"vault_id": "unique_vault_id",
"name": "My Vault",
"parties": [
"party_1_id",
"party_2_id",
"Vultisigner"
],
"policy": {},
"shares": {},
"created_at": "timestamp",
"updated_at": "timestamp"
}
Response (after keygen completion):
{
"vault_id": "unique_vault_id",
"name": "My Vault",
"parties": [
"party_1_id",
"party_2_id",
"Vultisigner"
],
"policy": {},
"shares": {
"ecdsa": [
{"party_id": "party_1_id"},
{"party_id": "party_2_id"},
{"party_id": "Vultisigner"
],
"eddsa": [
{"party_id": "party_1_id"},
{"party_id": "party_2_id"},
{"party_id": "Vultisigner"}
]
},
"created_at": "timestamp",
"updated_at": "timestamp"
}
Response (after keygen completion and has active transaction policy configured):
{
"vault_id": "unique_vault_id",
"name": "My Vault",
"parties": [
"party_1_id",
"party_2_id",
"Vultisigner"
],
"policy": {
"policy_id": "unique_policy_id",
"rules": [
{"condition": "wait_time", "value": "24 hours"},
{"condition": "pin_required", "value": true},
{"condition": "max_value", "value": "1000"}
]
},
"shares": {
"ecdsa": [
{"party_id": "party_1_id" },
{"party_id": "party_2_id" },
{"party_id": "Vultisigner" }
],
"eddsa": [
{"party_id": "party_1_id", "public_key": "public_key_data_1"},
{"party_id": "party_2_id", "public_key": "public_key_data_2"},
{"party_id": "Vultisigner", "public_key": "public_key_data_vultisigner"}
]
},
"created_at": "timestamp",
"updated_at": "timestamp"
}
Request an update to the transaction policy for a specific vault. This generates data that other parties must sign to approve the update. They should sign the contents of new_policy
and send the signatures to the /vault/{vault_id}/transaction_policies/{update_request_id}/approve
endpoint for it to take effect.
Request:
{
"new_policy": {
"policy_id": "unique_policy_id",
"rules": [
{"condition": "wait_time", "value": "24 hours"},
{"condition": "pin_required", "value": true},
{"condition": "max_value", "value": "1000"}
]
}
}
Response:
{
"update_request_id": "unique_request_id",
"status": "pending",
"new_policy": {
"policy_id": "unique_policy_id",
"rules": [
{"condition": "wait_time", "value": "24 hours"},
{"condition": "pin_required", "value": true},
{"condition": "max_value", "value": "1000"}
]
},
"requested_at": "timestamp"
}
Retrieve the details of a transaction policy update request. This allows the wallet to display the details of the request to the user and prompt them to sign it.
Response:
{
"update_request_id": "unique_request_id",
"status": "pending",
"new_policy": {
"policy_id": "unique_policy_id",
"rules": [
{"condition": "wait_time", "value": "24 hours"},
{"condition": "pin_required", "value": true},
{"condition": "max_value", "value": "1000"}
]
},
"requested_at": "timestamp"
}
Submit signatures to approve a transaction policy update request. The update will only take effect if a vault minimum threshold of signatures is reached. We will never be one of the signers.
Request:
{
"signatures": [
{"party_id": "party_1_id", "signature": "signature_data"},
{"party_id": "party_2_id", "signature": "signature_data"}
]
}
Response:
{
"status": "approved",
"new_policy": {
"policy_id": "unique_policy_id",
"rules": [
{"condition": "wait_time", "value": "24 hours"},
{"condition": "pin_required", "value": true},
{"condition": "max_value", "value": "1000"}
]
},
"approved_at": "timestamp"
}
Retrieve the history of transaction policies for a specific vault. This allows the wallet to notify if the policy has changed. Wallets can store the policy locally and compare it with the latest policy to determine if it has changed.
Response:
{
"policies": [
{
"policy_id": "policy_id_1",
"rules": [
{"condition": "wait_time", "value": "24 hours"},
{"condition": "pin_required", "value": true},
{"condition": "max_value", "value": "1000"}
],
"status": "inactive",
"created_at": "timestamp",
"modified_at": "timestamp"
},
{
"policy_id": "policy_id_2",
"rules": [
{"condition": "wait_time", "value": "12 hours"},
{"condition": "pin_required", "value": false},
{"condition": "max_value", "value": "500"}
],
"status": "inactive",
"created_at": "timestamp",
"modified_at": "timestamp"
}
]
}
Retrieve a specific transaction policy for a specific vault.
Response:
{
"policy_id": "policy_id_1",
"rules": [
{"condition": "wait_time", "value": "24 hours"},
{"condition": "pin_required", "value": true},
{"condition": "max_value", "value": "1000"}
],
"status": "inactive", // or "active"
"created_at": "timestamp",
"modified_at": "timestamp"
}
Request the signing of a transaction. This spins up a background job (using cmd/worker
with asynq
) to join the TSS-coordinator if the transaction policy allows it; otherwise, it will throw an error.
Request:
{
"transaction_details": {
"body": "transaction_body",
},
"policy_id": "unique_policy_id",
"session_id": "unique_session_id",
"pin_code": "user_provided_pin_code" // optional
}
Response (initial):
{
"transaction_id": "unique_transaction_id",
"status": "pending",
"required_signatures": [
{"party_id": "party_1_id", "status": "waiting"},
{"party_id": "party_2_id", "status": "waiting"},
{"party_id": "Vultisigner", "status": "waiting"}
],
"created_at": "timestamp"
}
Response (not allowed):
{
"transaction_id": "unique_transaction_id",
"status": "unauthorized",
"unauthorized_reason": "timelock_active", // or "pin_required", "max_value_exceeded"
"required_signatures": [
{"party_id": "party_1_id", "status": "waiting"},
{"party_id": "party_2_id", "status": "waiting"},
{"party_id": "Vultisigner", "status": "waiting"}
],
"created_at": "timestamp"
}
View the status of a transaction signing job.
Response:
{
"transaction_id": "unique_transaction_id",
"status": "in_progress", // or "completed", "failed" (TBD)
"required_signatures": [
{"party_id": "party_1_id", "status": "signed"},
{"party_id": "party_2_id", "status": "waiting"},
{"party_id": "Vultisigner", "status": "waiting"}
],
"updated_at": "timestamp"
}
Response (after keysign completion):
{
"transaction_id": "unique_transaction_id",
"status": "completed",
"signed_message": {
"body": "signed_message_body",
"hash": "signed_message_hash"
},
"required_signatures": [
{"party_id": "party_1_id", "status": "signed"},
{"party_id": "party_2_id", "status": "signed"},
{"party_id": "Vultisigner", "status": "signed"}
],
"created_at": "timestamp",
"updated_at": "timestamp"
}
This component handles the background jobs for key generation and transaction signing using Asynq.
- Triggered automatically upon vault creation.
- Joins the key generation session using the provided session ID.
- Stores the key generation data securely upon completion.
- Triggered when a transaction signing request is made.
- Joins the transaction signing session using the provided session ID.
- Waits for all parties to join and complete the signing process.
Both key generation and transaction signing jobs can be monitored via the GET
endpoints in cmd/vultisigner
.
Binance-TSS involves multiple parties participating in cryptographic operations. The TSS Coordinator facilitates communication between parties, especially useful for mobile devices that typically do not allow inbound connections. The session ID ensures isolation of different vault sessions.
The encryption package handles the encryption and decryption of sensitive data fields, ensuring secure storage.
-
Encrypt
- Encrypts plaintext data using AES encryption with GCM mode.
- Generates a nonce for each encryption operation.
- Uses a password from the server configuration for encryption.
-
Decrypt
- Decrypts ciphertext data encrypted by the Encrypt method.
- Requires the same password used for encryption.
-
Encryption of Sensitive Fields
- Sensitive fields (e.g.,
Xi
fields inEcdsaLocalData
andEddsaLocalData
) are encrypted before storage. - Ensures data security even if the database is compromised.
- Sensitive fields (e.g.,
-
Decryption of Sensitive Fields
- Converts encrypted data back to plaintext when accessed.
- Ensures secure and controlled access to sensitive data.
- Vault Share Encryption
- Validate sensitive fields.
- Encrypt
Xi
fields using theEncrypt
method. - Store the encrypted data in the database.
- Ensures sensitive information remains protected even if the database is compromised.