Soda Labs
Soda Labs Docs

Get decryption

Fetch a decrypted result and its MPC signatures directly, without relying on a relayer.

Why GetDecryption Exists

When a contract calls RequestDecryption on-chain, Bubble’s off-chain system produces the decrypted result along with MPC signatures. Typically, a relayer facilitates delivering this result to the chain by submitting a transaction that calls a contract’s callback with the decrypted data and signatures. This process ensures the flow ends with an on-chain transaction delivering the result.

GetDecryption offers an alternative to this flow. A backend can use the GetDecryption gRPC to receive the transaction payload (the calldata for the callback) - the same data the relayer uses. This method is useful if a user prefers handling the callback transaction themselves, wants to control when and how to submit the transaction, or needs the decrypted data off-chain for purposes like UX, analytics, or their own relayer logic.

How to Use GetDecryption

  1. Initiating Decryption:
    • A user contract initiates a decryption request by calling RequestDecryption with a decrypt ID, handles to decrypt, and the callback selector.
    • Bubble captures this event, processes the decryption, and stores the result indexed by chain ID, contract address, and decrypt ID.
  2. Fetching Result via GetDecryption:
    • A backend can call GetDecryption gRPC using the same chain ID, contract address, and decrypt ID.
    • If the decryption is not ready, Bubble returns an error indicating that the result is pending.
    • Once ready, Bubble provides the transaction data: the encoded call to the user contract’s callback with the decrypt ID, decrypted outputs, and MPC signatures.
  3. Utilizing the Result:
    • A user can then submit a transaction using this data with thier own relayer or directly via a wallet.
    • Use the payload to trigger thier own logic or know when the result is ready.
    • Employ the returned information off-chain as needed.

GetDecryption offers flexibility in managing and utilizing decryption results, providing the same data the relayer accesses.


Using GetDecryption in an Applicationn

As an application developer, integrating GetDecryption involves a series of interactions primarily centered around two main actions: RequestDecryption and GetDecryption. Below is a breakdown of the process.

Process Flow

Step 1: Initiate Decryption

  1. Invoke RequestDecryption:
    • A user contract calls RequestDecryption on the Bubble DecryptionCaller abstrast contract with the handles to decrypt and a callback selector for receiving decrypted data.
    • The function returns a decrypt ID (e.g., a counter or unique ID).

Step 2: Decryption Process

  • Handling by Bubble:
    • Bubble observes the GCDecryptionRequest event.
    • The Bubble sequencer and MPC evaluators begin decryption.
    • Decrypted outputs and MPC signatures are stored in the Bubble database, keyed by:
      • Chain ID
      • A contract address
      • Decrypt ID

Step 3: Retrieve Decryption Result

  1. Call GetDecryption:
    • A backend or an off-chain component retrieves the result by providing:
      • Chain ID
      • Contract's address
      • The decrypt ID
  2. Database Check by Bubble:
    • If not ready, an error is returned.
    • If ready, the transaction data (tx data) for a callback to the user contract is returned, with the decrypted outputs and MPC signatures.

Step 4: Submit Decryption On-chain

  • Submit the Transaction:
    • Construct and submit an on-chain transaction using the tx data from GetDecryption.

    • The contract’s callback function receives:

      • The decrypt ID
      • Decrypted outputs
      • MPC signatures

      The callback function calls verifyCallback modifier that verify the given inputs.

Step 5: Application Logic

  • Post-Processing:
    • Update state or emit events based on decrypted values.

Summary

  • RequestDecryption asks Bubble to decrypt specific handles and defines how to receive results on-chain.
  • GetDecryption allows off-chain components to confirm completion and obtain necessary transaction payloads to deliver decrypted results safely and verifiably back to the user contract.

Code and scripts

Decryption of handle is performed through a gRPC function:

rpc GetDecryption(GetDecryptionRequest) returns (GetDecryptionResponse);

message GetDecryptionRequest {
    int64 chain_id = 1;
    bytes contract_address = 2;
    bytes user_decrypt_id = 3; // Serialize big.Int to bytes
}

message GetDecryptionResponse {
    bytes tx_data = 1;
    int32 result_code = 2;
}

The following Python code demonstrates how to perform an Get Decryption RPC request:

  • client: A gRPC client.
  • chain_id and contract_address: Origin of the decryption request created
  • user_decrypt_id: ID of the decryption request
def get_decrypt_data(client, chain_id, contract_address, user_decrypt_id):

    # Convert contract address to bytes if needed
    if isinstance(contract_address, str):
        contract_address_bytes = to_bytes(hexstr=contract_address)
    else:
        contract_address_bytes = contract_address

    # Convert user decrypt ID to bytes if needed
    if isinstance(user_decrypt_id, int):
        user_decrypt_id_bytes = user_decrypt_id.to_bytes(32, byteorder='big')
    else:
        user_decrypt_id_bytes = user_decrypt_id

    # Call the gRPC service to get the decrypt data
    request = pb.GetDecryptionRequest(
		chain_id=int(chain_id),
		contract_address=contract_address_bytes,
		user_decrypt_id=user_decrypt_id_bytes
	)
    response = client.GetDecryption(request)

    logging.info(f"GetDecryption returned {len(response.tx_data)} bytes")

    if not response.tx_data:
        raise ValueError("Invalid response: tx_data is required")

    if response.result_code != 0:
        raise ValueError(f"Failed to get decryption data: {response.result_code}")

    return response.tx_data

Bubble supplies scripts that perform get decryption and send the tx data to the calling contract.

  • Script: The decrypt_request script, written in Python, is used to obtain decryption transaction data and forward it to the contract.
  • Inputs:
    • --contract_address flag:
      • Specify the contract address.
    • --user_decrypt_id flag:
      • Supplies the decrypt ID of the original request.
  • Output: The script retrieves the decryption transaction data from Bubble and initiates a transaction with this data. If no data is available, it will raise an exception.
  • Execution: Run the following command in the main Bubble directory:
python3 -m lib.offchain.decryptRequest.python.decrypt_request --contract_address=[address] --user_decrypt_id=[id]

On this page