webb-tools / tangle

Modular restaking infrastructure for developers, restakers, and operators.
https://www.tangle.tools/
GNU General Public License v3.0
51 stars 22 forks source link

[TASK] Signing rules for MPC jobs #289

Closed 1xstj closed 9 months ago

1xstj commented 10 months ago

Description:

We want to allow users to submit signing rules with job submissions, these signing rules should dictate who/what proposals can be signed by an already existing DKG/zkSaas phase one result.

  +---------------------------------------+
    | Data to sign (phase 2 job)          |
  +---------------------------------------+
                    |
                    v
             +--------------+
             | Signing rules|
             +--------------+
                    |
                    v
            +-----------------+
            |   Allow/Deny    |
            +-----------------+

Tasks

Possible solutions and challenges:

  1. User provides a smart contract address that acts as a verifier contract
+------------------------+
|          Job           |
+------------------------+
          |
          v
+-----------------------------+
|  Fetch verifier contract   |
+-----------------------------+
          |
          v
+----------------------------+
| Submit job to verifier     |
+----------------------------+
          |
    +-----+-----+
    |           |
    v           v
+--------------+  +--------------+
|  Approve    |  |  Deny       |
+--------------+  +--------------+

The submitted contract can be an extension of

// Base contract with a function for verifying jobs
// Other contracts will inherit from this and implement the verifyJob function

pragma solidity ^0.8.0;

abstract contract BaseVerifier {
    // Function to be implemented by derived contracts
    function verifyJob(bytes calldata jobData) external view virtual returns (bool);
}

Challenge: This requires communication from substrate_pallet -> evm, not sure if a feasible solution exists for this yet.

  1. User provides a caller address to the job, the caller address is a evm smart contract
+------------------------+
|          Job           |
+------------------------+
          |
          v
+-----------------------------+
|  Check allowed caller       |
|        address              |
+-----------------------------+
          |
    +-----+-----+
    |           |
    v           v
+--------------+  +--------------+
|   Allow      |  |   Deny       |
+--------------+  +--------------+

The jobs pallet can then add a precompile that allows submitting jobs to the pallet via evm contracts. The precompile will then allow only the known caller address to submit jobs

Example :

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SubstrateJobPrecompile {
    // Address of the Substrate jobs pallet
    address public substrateJobsPallet;

    // Event emitted upon successful job submission
    event JobSubmitted(address indexed sender, bytes32 indexed jobId, bytes jobData);

    // Constructor to set the address of the Substrate jobs pallet
    constructor(address _substrateJobsPallet) {
        substrateJobsPallet = _substrateJobsPallet;
    }

    // Function to submit a job to the Substrate jobs pallet
    function submitJob(bytes32 jobId, bytes calldata jobData) external {
        // Call the precompile in Substrate
        bool success;
        bytes memory result;
        (success, result) = substrateJobsPallet.call(abi.encodeWithSignature("submitJob(bytes32,bytes)", jobId, jobData));

        // Emit event based on the result
        if (success) {
            emit JobSubmitted(msg.sender, jobId, jobData);
        } else {
            revert("Job submission failed");
        }
    }
}

Challenges : This is easier to implement than the first approach but can become too confusing for the average user, also charging the user/contract for jobs can be complicated

drewstone commented 10 months ago

I'm not entirely sure the spec above has been updated or reflects a route that feels confident in its approach. Would welcome collaborating here @1xstj if you're stuck or updating this task as needed.

There are a few basic signing rules we need to build to make operating a private bridge as seamless as it was before.

In our private bridge we had 2 types of proposals

  1. AnchorUpdateProposals were voted upon by relayers.
  2. Governance proposals were voted upon by democracy pallet.

These two signing rules were used in a single application. We should ensure that we can make this possible using a smart contract and the permitted_caller structure.

Proposal

The permitted_caller is an EVM smart contract address. We leverage a precompile to submit a job (as above) from different functions on our permitted_caller contract.

contract PermittedCallerContract is GenericVotingContract {
    address[] public voters;
    uint256 public threshold;

    public function submitGovernanceProposal(bytes32 phase1JobId, bytes jobDetails) {
        // Validate the governance proposal
        require(jobPrecompile.hasProposalBeenExecuted(phase1JobId, jobDetails);
        // Execute it
        jobPrecompile.submit(phase1JobId, jobDetails);
    }

    public function submitAnchorUpdateProposal(bytes32 phase1JobId, bytes jobDetails) {
        // Validate the job/details are AUP
        require(validateJob(phase1JobId, jobDetails), "Invalid job details");
        // Check that we have received enough votes for the anchor update proposal.
        // This custom logic must be implemented by this contract, i.e. voting, tallying, storage.
        require(votesForProposal(keccak256(phase1jobId, jobDetails)) >= threshold, "Not enough votes");
        jobPrecompile.submit(phase1JobId, jobDetails);
    }
}