Skip to content

Commit

Permalink
Mock the confidential datastore for Foundry (#40)
Browse files Browse the repository at this point in the history
* Scaffold the solution+

* Use mock confidential store

* Remove view funcç

* Clean PR

* Fix example

* Add signatures in a better way·
  • Loading branch information
ferranbt authored Feb 6, 2024
1 parent 264f908 commit 983bab9
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 64 deletions.
12 changes: 6 additions & 6 deletions src/Test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ interface ConfidentialInputsWrapperI {
function resetConfidentialInputs() external;
}

interface ConfidentialStoreI {
function reset() external;
}

contract SuaveEnabled is Test {
ConfidentialInputsWrapperI constant confInputsWrapper = ConfidentialInputsWrapperI(Suave.CONFIDENTIAL_INPUTS);
ConfidentialStoreI constant confStoreWrapper = ConfidentialStoreI(Registry.confidentialStoreAddr);

function setUp() public {
string[] memory inputs = new string[](3);
Expand Down Expand Up @@ -82,11 +87,6 @@ contract SuaveEnabled is Test {
}

function resetConfidentialStore() public {
string[] memory inputs = new string[](3);
inputs[0] = "suave-geth";
inputs[1] = "forge";
inputs[2] = "reset-conf-store";

vm.ffi(inputs);
confStoreWrapper.reset();
}
}
2 changes: 0 additions & 2 deletions src/Transactions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ library Transactions {

function signTxn(Transactions.EIP1559Request memory request, string memory signingKey)
internal
view
returns (Transactions.EIP1559 memory response)
{
bytes memory rlp = Transactions.encodeRLP(request);
Expand All @@ -325,7 +324,6 @@ library Transactions {

function signTxn(Transactions.EIP155Request memory request, string memory signingKey)
internal
view
returns (Transactions.EIP155 memory response)
{
bytes memory rlp = Transactions.encodeRLP(request);
Expand Down
84 changes: 84 additions & 0 deletions src/forge/ConfidentialStore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.8;

import "../suavelib/Suave.sol";
import "forge-std/Test.sol";

// ConfidentialStore is an implementation of the confidential store in Solidity.
contract ConfidentialStore is Test {
mapping(bytes32 => Suave.DataRecord[]) private dataRecordsByConditionAndNamespace;
mapping(Suave.DataId => mapping(string => bytes)) private dataRecordsContent;
mapping(Suave.DataId => Suave.DataRecord) private dataRecords;

uint64 private numRecords;

type DataId is bytes16;

constructor() {
vm.record();
}

function newDataRecord(
uint64 decryptionCondition,
address[] memory allowedPeekers,
address[] memory allowedStores,
string memory dataType
) public returns (Suave.DataRecord memory) {
numRecords++;

// Use a counter of the records to create a unique key
Suave.DataId id = Suave.DataId.wrap(bytes16(keccak256(abi.encodePacked(numRecords))));
numRecords++;

Suave.DataRecord memory newRecord;
newRecord.id = id;
newRecord.decryptionCondition = decryptionCondition;
newRecord.allowedPeekers = allowedPeekers;
newRecord.allowedStores = allowedStores;
newRecord.version = dataType;

// Store the data record metadata
dataRecords[id] = newRecord;

// Use a composite index to store the records for the 'fetchDataRecords' function
bytes32 key = keccak256(abi.encodePacked(decryptionCondition, dataType));
dataRecordsByConditionAndNamespace[key].push(newRecord);

return newRecord;
}

function fetchDataRecords(uint64 cond, string memory namespace) public view returns (Suave.DataRecord[] memory) {
bytes32 key = keccak256(abi.encodePacked(cond, namespace));
return dataRecordsByConditionAndNamespace[key];
}

function confidentialStore(Suave.DataId dataId, string memory key, bytes memory value) public {
address[] memory allowedStores = dataRecords[dataId].allowedStores;
for (uint256 i = 0; i < allowedStores.length; i++) {
if (allowedStores[i] == msg.sender || allowedStores[i] == Suave.ANYALLOWED) {
dataRecordsContent[dataId][key] = value;
return;
}
}

revert("Not allowed to store");
}

function confidentialRetrieve(Suave.DataId dataId, string memory key) public view returns (bytes memory) {
address[] memory allowedPeekers = dataRecords[dataId].allowedPeekers;
for (uint256 i = 0; i < allowedPeekers.length; i++) {
if (allowedPeekers[i] == msg.sender || allowedPeekers[i] == Suave.ANYALLOWED) {
return dataRecordsContent[dataId][key];
}
}

revert("Not allowed to retrieve");
}

function reset() public {
(, bytes32[] memory writes) = vm.accesses(address(this));
for (uint256 i = 0; i < writes.length; i++) {
vm.store(address(this), writes[i], 0);
}
}
}
47 changes: 47 additions & 0 deletions src/forge/ConfidentialStoreConnector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.8;

import "./ConfidentialStore.sol";
import "../suavelib/Suave.sol";

contract ConfidentialStoreConnector {
fallback() external {
address confidentialStoreAddr = 0x0101010101010101010101010101010101010101;

address addr = address(this);
bytes4 sig;

if (addr == Suave.CONFIDENTIAL_STORE) {
sig = ConfidentialStore.confidentialStore.selector;
} else if (addr == Suave.CONFIDENTIAL_RETRIEVE) {
sig = ConfidentialStore.confidentialRetrieve.selector;
} else if (addr == Suave.FETCH_DATA_RECORDS) {
sig = ConfidentialStore.fetchDataRecords.selector;
} else if (addr == Suave.NEW_DATA_RECORD) {
sig = ConfidentialStore.newDataRecord.selector;
} else {
revert("function signature not found in the confidential store");
}

bytes memory input = msg.data;

// call 'confidentialStore' with the selector and the input data.
(bool success, bytes memory output) = confidentialStoreAddr.call(abi.encodePacked(sig, input));
if (!success) {
revert("Call to confidentialStore failed");
}

if (addr == Suave.CONFIDENTIAL_RETRIEVE) {
// special case we have to unroll the value from the abi
// since it comes encoded as tuple() but we return the value normally
// this was a special case that was not fixed yet in suave-geth.
output = abi.decode(output, (bytes));
}

assembly {
let location := output
let length := mload(output)
return(add(location, 0x20), length)
}
}
}
19 changes: 19 additions & 0 deletions src/forge/Registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import "../suavelib/Suave.sol";
import "./Connector.sol";
import "./ConfidentialInputs.sol";
import "./SuaveAddrs.sol";
import "./ConfidentialStore.sol";
import "./ConfidentialStoreConnector.sol";

interface registryVM {
function etch(address, bytes calldata) external;
}

library Registry {
registryVM constant vm = registryVM(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
address public constant confidentialStoreAddr = 0x0101010101010101010101010101010101010101;

function enable() public {
// enable all suave libraries
Expand All @@ -22,7 +25,23 @@ library Registry {
vm.etch(addrList[i], type(Connector).runtimeCode);
}

// enable the confidential store
deployCodeTo(type(ConfidentialStore).creationCode, confidentialStoreAddr);

// enable the confidential inputs wrapper
vm.etch(Suave.CONFIDENTIAL_RETRIEVE, type(ConfidentialStoreConnector).runtimeCode);
vm.etch(Suave.CONFIDENTIAL_STORE, type(ConfidentialStoreConnector).runtimeCode);
vm.etch(Suave.NEW_DATA_RECORD, type(ConfidentialStoreConnector).runtimeCode);
vm.etch(Suave.FETCH_DATA_RECORDS, type(ConfidentialStoreConnector).runtimeCode);

// enable is confidential wrapper
vm.etch(Suave.CONFIDENTIAL_INPUTS, type(ConfidentialInputsWrapper).runtimeCode);
}

function deployCodeTo(bytes memory creationCode, address where) internal {
vm.etch(where, creationCode);
(bool success, bytes memory runtimeBytecode) = where.call("");
require(success, "StdCheats deployCodeTo(string,bytes,uint256,address): Failed to create runtime bytecode.");
vm.etch(where, runtimeBytecode);
}
}
2 changes: 1 addition & 1 deletion src/protocols/Bundle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ library Bundle {
bytes[] txns;
}

function sendBundle(string memory url, BundleObj memory bundle) internal view returns (bytes memory) {
function sendBundle(string memory url, BundleObj memory bundle) internal returns (bytes memory) {
Suave.HttpRequest memory request = encodeBundle(bundle);
request.url = url;
return Suave.doHTTPRequest(request);
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/MevShare.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ library MevShare {
return request;
}

function sendBundle(string memory url, Bundle memory bundle) internal view {
function sendBundle(string memory url, Bundle memory bundle) internal {
Suave.HttpRequest memory request = encodeBundle(bundle);
request.url = url;
Suave.doHTTPRequest(request);
Expand Down
Loading

0 comments on commit 983bab9

Please sign in to comment.