Soda Labs
Soda Labs Docs
Developer GuidesTypes and operations

Decryption

The controlled decryption flow in Bubble: requesting a decryption, and receiving a verified result in a callback.

Overview: What Decryption Means in Bubble

In Bubble, all user data and computation results are stored and processed in encrypted form, represented on-chain as handles (references to garbledtexts).
When a user or contract needs to see the plaintext value, it must go through a secure decryption flow.

In this process, no single party can decrypt data alone. Decryption is coordinated among multiple MPC parties using garbled circuit and signature protocols and verified on-chain.

Types of Decryption

There are two main types of decryption:

  1. Decrypt for Everyone:
    This results in the decryption output being saved on-chain, allowing anyone to view it. In Bubble, this is implemented using the requestDecryption function from our Solidity library.
  2. Decrypt for a Specific User:
    The decryption result is encrypted using the user's AES key, ensuring only that user can access it. This method is done via the User Interactor directly and detailed in the Encrypt to user section.

Onchain decryption key features

DecryptionCaller abstract contract

To decrypt a garbledtext handle, a developer should use the decryptionRequest function in the Bubble Solidity library.

To simplify the decryption process, we provide an abstract contract named DecryptionCaller. Smart contract developers can derive from this contract, which offers the following core functions:

  1. requestDecryption
    • Description: Accepts a list of handles that require decryption and a function selector for returning the decryption results.
    • Returns: A decryption ID used to identify the request.
  2. checkCallbackHandles
    • Description: Takes a decryption ID, a list of outputs, and signatures. It verifies if the signatures are valid for the request's handles and outputs.
    • Returns: true if thecallback inputs are valid; otherwise, false.

More detailed on the decryptionCaller contract can be found here.


Callback function

A callback function is a function that is automatically called after another operation finishes, usually to continue the flow once some external process completes.

In Bubble, callback functions are mainly used in the decryption flow:
when a smart contract requests a decryption, it provides a callback selector, the function that should be called once the decryption is done.

On Chain Decryption Flow

On-chain decryption is an asynchronous procedure comprising several key steps:

  1. Decryption requests on-chain: The smart contract requests decryption.
  2. Decryption Execution: The bubble system catches the decryption request, processes it and executes a transaction to the callback function.
  3. Verification of the result - on-chain: The contract receives the outputs, and ensures the integrity and authenticity of the decrypted data.

This systematic approach ensures efficient and secure data decryption on the blockchain.

Here is a code example that explains the decryption flow:

contract user_contract is DecryptionCaller {

    gtUint256 add256;
    gtUint256 onboard256;

    function add256Bit(itUint256 calldata it) public {
        gtUint256 a = MpcCore.setPublic256(1809251394333065553493296640760748560207343510400633813116524750123642650623);
        gtUint256 b = MpcCore.setPublic256(1);
        add256 = MpcCore.add(a, b);
        MpcCore.permitThis(add256);
        onboard256 = MpcCore.validateCiphertext(it);
        MpcCore.permitThis(onboard256);

        uint256[] memory arr = new uint256[](2);
        arr[0] = gtUint256.unwrap(add256);
        arr[1] = gtUint256.unwrap(onboard256);

        requestDecryption(arr, this.callback256Bit.selector); // Call the abstract contract' method
    }

    function callback256Bit(uint256 decryptID, bytes[] calldata output,
                            bytes[] calldata signatures)
                            public verifyCallback(decryptID, output, signatures) {
        // Handle the callback from the MPC core
        add256Result = abi.decode(output[0], (uint256));
        onboard256Result = abi.decode(output[1], (uint256));
    }
}

1. Requests Decryption - On-chain

To initiate decryption, the smart contract first aggregates all handles into a single array. It then calls requestDecryption from the abstract DecryptionCaller contract, ensuring to provide a callback function selector. This abstract method utilizes the Bubble Solidity library, which subsequently calls the GCHandler function to emit the request decryption event.

2. MPC Execution

When a decryption request event is emitted on-chain, the connector listens for the event, captures its data, and then forwards it to the MPC network for secure processing.

Each MPC party collaborates to perform the decryption using its private key share and then signs the resulting plaintext with its unique EIP-712 signature.
Once the signatures are collected, the connector aggregates these signed results and sends them back to the originating contract.

4. Callback Execution

When the callback function specified in the initial request is executed, it receives the decryption output, the signatures, and the decryption request ID. This function utilizes the DecryptionCaller abstract contract to validate the inputs by invoking the verifyCallback modifier. Once verification is successful, the function can utilize the decryption results to update state, initiate further logic, or emit new events - thereby completing the secure decryption round-trip between the blockchain and the MPC network.

Off Chain Decryption Flow

In addition to on-chain decryption, you can obtain decryption data off-chain through the Bubble user interactor. For more details, refer to the Get Decryption section. This off-chain method allows users to manage decryption processes more flexibly without relying solely on the bubble relayer and the callback function.

On this page