Skip to content

Commit

Permalink
feat: update EVM Hooks (#755)
Browse files Browse the repository at this point in the history
  • Loading branch information
shekohex authored Sep 5, 2024
1 parent ebee38e commit d8cecdf
Show file tree
Hide file tree
Showing 15 changed files with 151 additions and 183 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "forge/lib/openzeppelin-contracts"]
path = forge/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "forge/lib/tnt-core"]
path = forge/lib/tnt-core
url = https://github.com/webb-tools/tnt-core
8 changes: 5 additions & 3 deletions forge/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ out = "out"
libs = ["lib"]

remappings = [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"core/=lib/tnt-core/src/",
"@/=src/",
]

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
1 change: 1 addition & 0 deletions forge/lib/tnt-core
Submodule tnt-core added at 37f34c
20 changes: 0 additions & 20 deletions forge/src/Runtime.sol

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,26 @@
// DO NOT USE THIS IN PRODUCTION, IT IS JUST FOR TESTING.
pragma solidity >=0.8.3;

import "../hooks/RegisterationHook.sol";
import "../hooks/RequestHook.sol";
// import "../JobResultVerifier.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "core/BlueprintServiceManager.sol";

contract CGGMP21RegistrationHook is RegistrationHook {
/// A Simple List of all Participants Ecdsa Public Keys
struct Participant {
contract CGGMP21Blueprint is BlueprintServiceManager {
/// A Simple List of all Operator Ecdsa Public Keys
struct Operator {
address addr;
bytes publicKey;
}

Participant[] public participants;

error InvalidRegistrationInputs();

function onRegister(bytes calldata participant, bytes calldata registrationInputs)
public
payable
override
onlyRuntime
{
// The inputs are empty, we don't need them.
if (registrationInputs.length != 0) {
revert InvalidRegistrationInputs();
}
address addr = address(uint160(uint256(keccak256(participant))));
// add the participant to the list
participants.push(Participant(addr, participant));
}
}

contract CGGMP21RequestHook is RequestHook {
uint8 constant KEYGEN_JOB = 0;
uint8 constant SIGNING_JOB = 1;

Operator[] public blueprintOperators;

struct Service {
/// The id of the service
uint64 id;
/// The public keys of participants of the service
bytes[] participants;
/// The list of participants of the service
Operator[] operators;
}

// Keygens
Expand All @@ -57,38 +36,48 @@ contract CGGMP21RequestHook is RequestHook {
error KeygenJobNotFound();
error InvalidJob();

/// Stores the list of services that are requested
function onRequest(uint64 serviceId, bytes[] calldata participants, bytes calldata requestInputs)
function onRegister(bytes calldata operator, bytes calldata registrationInputs)
public
payable
override
onlyRuntime
onlyFromRootChain
{
address addr = operatorAddressFromPublicKey(operator);
// add the participant to the list
blueprintOperators.push(Operator(addr, operator));
}

function onRequest(uint64 serviceId, bytes[] calldata operators, bytes calldata requestInputs)
public
payable
override
onlyFromRootChain
{
// The requestInputs are empty, we don't need them.
if (requestInputs.length != 0) {
revert InvalidRequestInputs();
}
// initialize the service
Service memory service;
// set the id of the service
// Create the service
Service storage service = services[serviceId];
service.id = serviceId;
// set the participants of the service
service.participants = participants;
// store the service
services[serviceId] = service;

for (uint256 i = 0; i < operators.length; i++) {
address addr = operatorAddressFromPublicKey(operators[i]);
service.operators.push(Operator(addr, operators[i]));
}
}

function onJobCall(uint64 serviceId, uint8 job, uint64 jobCallId, bytes calldata inputs)
public
payable
override
onlyRuntime
onlyFromRootChain
{
// Job 0 is the Keygen Job
if (job == KEYGEN_JOB) {
// The inputs are the DKG threshold
(uint8 t) = abi.decode(inputs, (uint8));
uint256 n = services[serviceId].participants.length;
uint256 n = services[serviceId].operators.length;
// verify the DKG threshold is valid
if (t == 0 || t > n) {
revert InvalidDKGThreshold();
Expand All @@ -97,7 +86,7 @@ contract CGGMP21RequestHook is RequestHook {
keygens[serviceId][jobCallId] = t;
} else if (job == SIGNING_JOB) {
// inputs are keygenJobCallId and message hash (32 bytes)
(uint64 keygenJobCallId, bytes32 message) = abi.decode(inputs, (uint64, bytes32));
(uint64 keygenJobCallId, bytes32 _message) = abi.decode(inputs, (uint64, bytes32));
// verify the keygen job exists
if (keygens[serviceId][keygenJobCallId] == 0) {
revert KeygenJobNotFound();
Expand All @@ -106,6 +95,10 @@ contract CGGMP21RequestHook is RequestHook {
revert InvalidJob();
}
}

function operatorAddressFromPublicKey(bytes calldata publicKey) public pure returns (address) {
return address(uint160(uint256(keccak256(publicKey))));
}
}

// contract CGGMP21JobResultVerifier is JobResultVerifier {
Expand Down
19 changes: 0 additions & 19 deletions forge/src/hooks/RegisterationHook.sol

This file was deleted.

33 changes: 0 additions & 33 deletions forge/src/hooks/RequestHook.sol

This file was deleted.

4 changes: 2 additions & 2 deletions forge/test/SigningRules.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../src/SigningRules.sol";
import {Proposal, ProposalStatus} from "../src/SigningRules.sol";
import "@/SigningRules.sol";
import {Proposal, ProposalStatus} from "@/SigningRules.sol";

contract VotableSigningRules is SigningRules {
function _isVotableProposal(uint64 phase1JobId, bytes memory phase2JobDetails)
Expand Down
93 changes: 90 additions & 3 deletions pallets/services/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl<T: Config> Pallet<T> {
name: String::from("onRegister"),
inputs: vec![
ethabi::Param {
name: String::from("participant"),
name: String::from("operator"),
kind: ethabi::ParamType::Bytes,
internal_type: None,
},
Expand Down Expand Up @@ -197,7 +197,7 @@ impl<T: Config> Pallet<T> {
JobResultVerifier::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("verify"),
name: String::from("onJobCallResult"),
inputs: vec![
ethabi::Param {
name: String::from("serviceId"),
Expand Down Expand Up @@ -253,6 +253,93 @@ impl<T: Config> Pallet<T> {
Ok((allowed, weight))
}

pub fn verify_job_call_result_hook(
job_def: &JobDefinition<T::Constraints>,
service_id: u64,
job: u8,
job_call_id: u64,
prefrences: &OperatorPreferences,
inputs: &[Field<T::Constraints, T::AccountId>],
outputs: &[Field<T::Constraints, T::AccountId>],
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let (allowed, weight) = match job_def.verifier {
JobResultVerifier::None => (true, Weight::zero()),
JobResultVerifier::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("verifyJobCallResult"),
inputs: vec![
ethabi::Param {
name: String::from("serviceId"),
kind: ethabi::ParamType::Uint(64),
internal_type: None,
},
ethabi::Param {
name: String::from("jobIndex"),
kind: ethabi::ParamType::Uint(8),
internal_type: None,
},
ethabi::Param {
name: String::from("jobCallId"),
kind: ethabi::ParamType::Uint(64),
internal_type: None,
},
ethabi::Param {
name: String::from("participant"),
kind: ethabi::ParamType::Bytes,
internal_type: None,
},
ethabi::Param {
name: String::from("inputs"),
kind: ethabi::ParamType::Bytes,
internal_type: None,
},
ethabi::Param {
name: String::from("outputs"),
kind: ethabi::ParamType::Bytes,
internal_type: None,
},
],
outputs: vec![ethabi::Param {
name: String::from("allowed"),
kind: ethabi::ParamType::Bool,
internal_type: None,
}],
constant: None,
state_mutability: ethabi::StateMutability::NonPayable,
};
let service_id = Token::Uint(ethabi::Uint::from(service_id));
let job = Token::Uint(ethabi::Uint::from(job));
let job_call_id = Token::Uint(ethabi::Uint::from(job_call_id));
let participant = prefrences.to_ethabi().first().unwrap().clone();
let inputs = Token::Bytes(Field::encode_to_ethabi(inputs));
let outputs = Token::Bytes(Field::encode_to_ethabi(outputs));
let data = call
.encode_input(&[service_id, job, job_call_id, participant, inputs, outputs])
.map_err(|_| Error::<T>::EVMAbiEncode)?;
let gas_limit = 300_000;

let info =
Self::evm_call(Self::address(), contract, U256::from(0), data, gas_limit)?;
// decode the result
let allowed = match info.exit_reason.is_succeed().then_some(&info.value) {
Some(data) => {
let result =
call.decode_output(data).map_err(|_| Error::<T>::EVMAbiDecode)?;
let allowed = result.first().ok_or_else(|| Error::<T>::EVMAbiDecode)?;
match allowed {
Token::Bool(allowed) => *allowed,
_ => return Err(Error::<T>::EVMAbiDecode.into()),
}
},
None => false,
};
(allowed, Self::weight_from_call_info(&info))
},
};
Ok((allowed, weight))
}

pub fn evm_call(
from: H160,
to: H160,
Expand All @@ -279,7 +366,7 @@ impl<T: Config> Pallet<T> {
if info.exit_reason.is_revert() {
log::debug!(
target: "evm",
"Call to: {:?} with data: 0x{} Reverted with reason: 0x{}",
"Call to: {:?} with data: 0x{} Reverted with reason: (0x{})",
to,
hex::encode(&data),
hex::encode(&info.value),
Expand Down
2 changes: 2 additions & 0 deletions pallets/services/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ pub mod module {
JobCallResultNotFound,
/// An error occurred while encoding the EVM ABI.
EVMAbiEncode,
/// An error occurred while decoding the EVM ABI.
EVMAbiDecode,
/// Operator profile not found.
OperatorProfileNotFound,
/// Maximum number of services per Provider reached.
Expand Down
Loading

0 comments on commit d8cecdf

Please sign in to comment.