From bfaf2ffc7f5aff4e098145a01775a5350530aa57 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Wed, 31 Jan 2024 09:20:21 +0000 Subject: [PATCH 1/6] Scaffold the solution+ --- src/forge/ConfidentialStore.sol | 138 +++++++++++++++++++++++++++++ src/forge/Registry.sol | 18 ++++ test/Forge.t.sol | 38 ++++++-- test/forge/ConfidentialStore.t.sol | 49 ++++++++++ 4 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 src/forge/ConfidentialStore.sol create mode 100644 test/forge/ConfidentialStore.t.sol diff --git a/src/forge/ConfidentialStore.sol b/src/forge/ConfidentialStore.sol new file mode 100644 index 0000000..efafb4c --- /dev/null +++ b/src/forge/ConfidentialStore.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.8; + +import "../suavelib/Suave.sol"; +import "forge-std/Test.sol"; + +contract ConfidentialStore is Test { + mapping(bytes32 => Suave.DataRecord[]) private dataRecordsByConditionAndNamespace; + mapping(Suave.DataId => mapping(string => bytes)) private dataRecordsContent; + uint64 private numRecords; + + type DataId is bytes16; + + function newDataRecord( + uint64 decryptionCondition, + address[] memory allowedPeekers, + address[] memory allowedStores, + string memory dataType + ) public returns (Suave.DataRecord memory) { + console.log("_ CREATE NEW RECORD *_"); + console.log(numRecords); + console.logBytes(abi.encodePacked(numRecords)); + console.logBytes32(keccak256(abi.encodePacked(numRecords))); + console.logBytes16(bytes16(keccak256(abi.encodePacked(numRecords)))); + numRecords++; + + console.log("_ STEP 0_"); + + // Use a counter of the records to create a unique key + Suave.DataId id = Suave.DataId.wrap(bytes16(keccak256(abi.encodePacked(numRecords)))); + numRecords++; + console.log("_ STEP 1 _"); + Suave.DataRecord memory newRecord; + newRecord.id = id; + newRecord.decryptionCondition = decryptionCondition; + newRecord.allowedPeekers = allowedPeekers; + newRecord.allowedStores = allowedStores; + newRecord.version = dataType; + console.log("_ STEP 2_"); + bytes32 key = keccak256(abi.encodePacked(decryptionCondition, dataType)); + console.log("A1"); + dataRecordsByConditionAndNamespace[key].push(newRecord); + console.log("A2"); + + 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 { + dataRecordsContent[dataId][key] = value; + } + + function confidentialRetrieve(Suave.DataId dataId, string memory key) public view returns (bytes memory) { + return dataRecordsContent[dataId][key]; + } +} + +contract ConfidentialStoreWrapper is Test { + fallback() external { + address confidentialStoreAddr = 0x0101010101010101010101010101010101010101; + + // use the origin address to figure out which function + // is being called + address addr = address(this); + bytes4 sig; + console.log("_ HERE _"); + + address[] memory allowedList = new address[](2); + allowedList[0] = address(this); + + ConfidentialStore store = ConfidentialStore(confidentialStoreAddr); + store.newDataRecord(0, allowedList, allowedList, "xx"); + + console.log(addr); + + if (addr == Suave.CONFIDENTIAL_STORE) { + // confidentialStore (0xd22a3b0b) + sig = 0xd22a3b0b; + } else if (addr == Suave.CONFIDENTIAL_RETRIEVE) { + // confidentialRetrieve (0xe3b417bc) + sig = 0xe3b417bc; + } else if (addr == Suave.FETCH_DATA_RECORDS) { + // fetchDataRecords (0xccb885c4) + sig = 0xccb885c4; + } else if (addr == Suave.NEW_DATA_RECORD) { + // newDataRecord (0xe3fbcfc3) + sig = 0xe3fbcfc3; + } else { + console.log("_ not found _ "); + revert("SUCCESS? function"); + } + + 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)); + console.log("_XXX__"); + console.log("_ SUCCESS? xx yy _"); + console.log(success); + console.logBytes(output); + + if (!success) { + revert("Call to confidentialStore failed"); + } + + assembly { + let location := output + let length := mload(output) + return(add(location, 0x20), length) + } + } +} + +contract Target { + uint64 public count; + + function incr() public returns (uint64) { + count++; + return count; + } +} + +contract TargetProxy { + Target target; + + constructor(address addr) { + target = Target(addr); + } + + fallback() external { + target.incr(); + } +} diff --git a/src/forge/Registry.sol b/src/forge/Registry.sol index b82a092..094f6e2 100644 --- a/src/forge/Registry.sol +++ b/src/forge/Registry.sol @@ -6,6 +6,7 @@ import "../suavelib/Suave.sol"; import "./Connector.sol"; import "./ConfidentialInputs.sol"; import "./SuaveAddrs.sol"; +import "./ConfidentialStore.sol"; interface registryVM { function etch(address, bytes calldata) external; @@ -13,6 +14,7 @@ interface registryVM { library Registry { registryVM constant vm = registryVM(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + address constant confidentialStoreAddr = 0x0101010101010101010101010101010101010101; function enable() public { // enable all suave libraries @@ -22,7 +24,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(ConfidentialStoreWrapper).runtimeCode); + vm.etch(Suave.CONFIDENTIAL_STORE, type(ConfidentialStoreWrapper).runtimeCode); + vm.etch(Suave.NEW_DATA_RECORD, type(ConfidentialStoreWrapper).runtimeCode); + vm.etch(Suave.FETCH_DATA_RECORDS, type(ConfidentialStoreWrapper).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); + } } diff --git a/test/Forge.t.sol b/test/Forge.t.sol index 0ca3ac1..2ab9d13 100644 --- a/test/Forge.t.sol +++ b/test/Forge.t.sol @@ -8,16 +8,27 @@ import "src/suavelib/Suave.sol"; contract TestForge is Test, SuaveEnabled { address[] public addressList = [0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829]; - function testConfidentialStore() public { - Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); + function testForgeConfidentialStore() public { + //Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); - bytes memory value = abi.encode("suave works with forge!"); - Suave.confidentialStore(record.id, "key1", value); + //console.log("_ RECORD FOUJN?"); - bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); - assertEq(keccak256(found), keccak256(value)); + //bytes memory value = abi.encode("suave works with forge!"); + //Suave.confidentialStore(record.id, "key1", value); + + Target target = new Target(); + TargetProxy proxy = new TargetProxy(address(target)); + + address proxyAddr = address(proxy); + proxyAddr.call(abi.encode()); + + console.log(target.count()); + + //bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); + //assertEq(keccak256(found), keccak256(value)); } + /* function testConfidentialReset() public { Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); @@ -32,8 +43,21 @@ contract TestForge is Test, SuaveEnabled { bytes memory found2 = Suave.confidentialRetrieve(record.id, "key1"); assertEq(found2.length, 0); } + */ + + /* + function testConfidentialReset() public { + Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); + + bytes memory value = abi.encode("suave works with forge!"); + Suave.confidentialStore(record.id, "key1", value); + + bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); + assertEq(keccak256(found), keccak256(value)); + } + */ - function testConfidentialInputs() public { + function testForgeConfidentialInputs() public { // ensure that the confidential inputs are empty bytes memory found = Suave.confidentialInputs(); assertEq(found.length, 0); diff --git a/test/forge/ConfidentialStore.t.sol b/test/forge/ConfidentialStore.t.sol new file mode 100644 index 0000000..3ddc8ad --- /dev/null +++ b/test/forge/ConfidentialStore.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "src/forge/ConfidentialStore.sol"; + +contract TestConfidentialStore is Test { + ConfidentialStore store; + address[] public addressList = [0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829]; + + function setUp() public { + store = new ConfidentialStore(); + } + + function testConfidentialStoreNewRecordAndFetch() public { + // This function tests that we can create new data records and fetch them + store.newDataRecord(0, addressList, addressList, "namespace"); + store.newDataRecord(0, addressList, addressList, "namespace1"); + store.newDataRecord(1, addressList, addressList, "namespace"); + + // fetch the records + Suave.DataRecord[] memory records = store.fetchDataRecords(0, "namespace"); + assertEq(records.length, 1); + + records = store.fetchDataRecords(0, "namespace1"); + assertEq(records.length, 1); + + records = store.fetchDataRecords(1, "namespace"); + assertEq(records.length, 1); + + // add more entries to 'namespace' + store.newDataRecord(0, addressList, addressList, "namespace"); + store.newDataRecord(0, addressList, addressList, "namespace"); + + records = store.fetchDataRecords(0, "namespace"); + assertEq(records.length, 3); + } + + function testConfidentialStoreStoreRetrieve() public { + // This function tests that we can store and retrieve a value from the record + Suave.DataRecord memory record = store.newDataRecord(0, addressList, addressList, "namespace"); + + bytes memory value = abi.encodePacked("value"); + store.confidentialStore(record.id, "key1", value); + + bytes memory found = store.confidentialRetrieve(record.id, "key1"); + assertEq(keccak256(found), keccak256(value)); + } +} From c875e32bc3d1e8ada1ef973d271177b4f406d18b Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 2 Feb 2024 16:27:08 +0000 Subject: [PATCH 2/6] Use mock confidential store --- src/Test.sol | 12 ++--- src/forge/ConfidentialStore.sol | 80 +++++++++--------------------- src/forge/Registry.sol | 2 +- src/protocols/Bundle.sol | 2 +- src/protocols/MevShare.sol | 2 +- src/suavelib/Suave.sol | 72 ++++++++++++--------------- test/Forge.t.sol | 43 ++++------------ test/forge/ConfidentialStore.t.sol | 34 ++++++++++++- 8 files changed, 108 insertions(+), 139 deletions(-) diff --git a/src/Test.sol b/src/Test.sol index 7b20508..660fba4 100644 --- a/src/Test.sol +++ b/src/Test.sol @@ -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(0x0101010101010101010101010101010101010101); function setUp() public { string[] memory inputs = new string[](3); @@ -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(); } } diff --git a/src/forge/ConfidentialStore.sol b/src/forge/ConfidentialStore.sol index efafb4c..4933cf3 100644 --- a/src/forge/ConfidentialStore.sol +++ b/src/forge/ConfidentialStore.sol @@ -11,36 +11,32 @@ contract ConfidentialStore is Test { 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) { - console.log("_ CREATE NEW RECORD *_"); - console.log(numRecords); - console.logBytes(abi.encodePacked(numRecords)); - console.logBytes32(keccak256(abi.encodePacked(numRecords))); - console.logBytes16(bytes16(keccak256(abi.encodePacked(numRecords)))); numRecords++; - console.log("_ STEP 0_"); - // Use a counter of the records to create a unique key Suave.DataId id = Suave.DataId.wrap(bytes16(keccak256(abi.encodePacked(numRecords)))); numRecords++; - console.log("_ STEP 1 _"); + Suave.DataRecord memory newRecord; newRecord.id = id; newRecord.decryptionCondition = decryptionCondition; newRecord.allowedPeekers = allowedPeekers; newRecord.allowedStores = allowedStores; newRecord.version = dataType; - console.log("_ STEP 2_"); + + // Use a composite index to store the records for the 'fetchDataRecords' function bytes32 key = keccak256(abi.encodePacked(decryptionCondition, dataType)); - console.log("A1"); dataRecordsByConditionAndNamespace[key].push(newRecord); - console.log("A2"); return newRecord; } @@ -57,57 +53,50 @@ contract ConfidentialStore is Test { function confidentialRetrieve(Suave.DataId dataId, string memory key) public view returns (bytes memory) { return dataRecordsContent[dataId][key]; } + + 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); + } + } } contract ConfidentialStoreWrapper is Test { fallback() external { address confidentialStoreAddr = 0x0101010101010101010101010101010101010101; - // use the origin address to figure out which function - // is being called address addr = address(this); bytes4 sig; - console.log("_ HERE _"); - - address[] memory allowedList = new address[](2); - allowedList[0] = address(this); - - ConfidentialStore store = ConfidentialStore(confidentialStoreAddr); - store.newDataRecord(0, allowedList, allowedList, "xx"); - - console.log(addr); + // You can use 'forge selectors list' to retrieve the function signatures if (addr == Suave.CONFIDENTIAL_STORE) { - // confidentialStore (0xd22a3b0b) sig = 0xd22a3b0b; } else if (addr == Suave.CONFIDENTIAL_RETRIEVE) { - // confidentialRetrieve (0xe3b417bc) sig = 0xe3b417bc; } else if (addr == Suave.FETCH_DATA_RECORDS) { - // fetchDataRecords (0xccb885c4) sig = 0xccb885c4; } else if (addr == Suave.NEW_DATA_RECORD) { - // newDataRecord (0xe3fbcfc3) sig = 0xe3fbcfc3; } else { - console.log("_ not found _ "); - revert("SUCCESS? function"); + revert("function signature not found in the confidential store"); } bytes memory input = msg.data; - // call 'confidentialStore' with the selector - // and the input data. + // call 'confidentialStore' with the selector and the input data. (bool success, bytes memory output) = confidentialStoreAddr.call(abi.encodePacked(sig, input)); - console.log("_XXX__"); - console.log("_ SUCCESS? xx yy _"); - console.log(success); - console.logBytes(output); - if (!success) { revert("Call to confidentialStore failed"); } + if (addr == Suave.CONFIDENTIAL_RETRIEVE) { + // special case we have to unloop 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) @@ -115,24 +104,3 @@ contract ConfidentialStoreWrapper is Test { } } } - -contract Target { - uint64 public count; - - function incr() public returns (uint64) { - count++; - return count; - } -} - -contract TargetProxy { - Target target; - - constructor(address addr) { - target = Target(addr); - } - - fallback() external { - target.incr(); - } -} diff --git a/src/forge/Registry.sol b/src/forge/Registry.sol index 094f6e2..09fee1f 100644 --- a/src/forge/Registry.sol +++ b/src/forge/Registry.sol @@ -14,7 +14,7 @@ interface registryVM { library Registry { registryVM constant vm = registryVM(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - address constant confidentialStoreAddr = 0x0101010101010101010101010101010101010101; + address public constant confidentialStoreAddr = 0x0101010101010101010101010101010101010101; function enable() public { // enable all suave libraries diff --git a/src/protocols/Bundle.sol b/src/protocols/Bundle.sol index 6d95ff9..68051f1 100644 --- a/src/protocols/Bundle.sol +++ b/src/protocols/Bundle.sol @@ -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); diff --git a/src/protocols/MevShare.sol b/src/protocols/MevShare.sol index 2990a20..7fe21f5 100644 --- a/src/protocols/MevShare.sol +++ b/src/protocols/MevShare.sol @@ -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); diff --git a/src/suavelib/Suave.sol b/src/suavelib/Suave.sol index bcaf758..9631364 100644 --- a/src/suavelib/Suave.sol +++ b/src/suavelib/Suave.sol @@ -95,8 +95,8 @@ library Suave { address public constant SUBMIT_ETH_BLOCK_TO_RELAY = 0x0000000000000000000000000000000042100002; // Returns whether execution is off- or on-chain - function isConfidential() internal view returns (bool b) { - (bool success, bytes memory isConfidentialBytes) = IS_CONFIDENTIAL_ADDR.staticcall(""); + function isConfidential() internal returns (bool b) { + (bool success, bytes memory isConfidentialBytes) = IS_CONFIDENTIAL_ADDR.call(""); if (!success) { revert PeekerReverted(IS_CONFIDENTIAL_ADDR, isConfidentialBytes); } @@ -110,10 +110,9 @@ library Suave { function buildEthBlock(BuildBlockArgs memory blockArgs, DataId dataId, string memory namespace) internal - view returns (bytes memory, bytes memory) { - (bool success, bytes memory data) = BUILD_ETH_BLOCK.staticcall(abi.encode(blockArgs, dataId, namespace)); + (bool success, bytes memory data) = BUILD_ETH_BLOCK.call(abi.encode(blockArgs, dataId, namespace)); if (!success) { revert PeekerReverted(BUILD_ETH_BLOCK, data); } @@ -121,8 +120,8 @@ library Suave { return abi.decode(data, (bytes, bytes)); } - function confidentialInputs() internal view returns (bytes memory) { - (bool success, bytes memory data) = CONFIDENTIAL_INPUTS.staticcall(abi.encode()); + function confidentialInputs() internal returns (bytes memory) { + (bool success, bytes memory data) = CONFIDENTIAL_INPUTS.call(abi.encode()); if (!success) { revert PeekerReverted(CONFIDENTIAL_INPUTS, data); } @@ -130,8 +129,8 @@ library Suave { return data; } - function confidentialRetrieve(DataId dataId, string memory key) internal view returns (bytes memory) { - (bool success, bytes memory data) = CONFIDENTIAL_RETRIEVE.staticcall(abi.encode(dataId, key)); + function confidentialRetrieve(DataId dataId, string memory key) internal returns (bytes memory) { + (bool success, bytes memory data) = CONFIDENTIAL_RETRIEVE.call(abi.encode(dataId, key)); if (!success) { revert PeekerReverted(CONFIDENTIAL_RETRIEVE, data); } @@ -139,15 +138,15 @@ library Suave { return data; } - function confidentialStore(DataId dataId, string memory key, bytes memory value) internal view { - (bool success, bytes memory data) = CONFIDENTIAL_STORE.staticcall(abi.encode(dataId, key, value)); + function confidentialStore(DataId dataId, string memory key, bytes memory value) internal { + (bool success, bytes memory data) = CONFIDENTIAL_STORE.call(abi.encode(dataId, key, value)); if (!success) { revert PeekerReverted(CONFIDENTIAL_STORE, data); } } - function doHTTPRequest(HttpRequest memory request) internal view returns (bytes memory) { - (bool success, bytes memory data) = DO_HTTPREQUEST.staticcall(abi.encode(request)); + function doHTTPRequest(HttpRequest memory request) internal returns (bytes memory) { + (bool success, bytes memory data) = DO_HTTPREQUEST.call(abi.encode(request)); if (!success) { revert PeekerReverted(DO_HTTPREQUEST, data); } @@ -155,8 +154,8 @@ library Suave { return abi.decode(data, (bytes)); } - function ethcall(address contractAddr, bytes memory input1) internal view returns (bytes memory) { - (bool success, bytes memory data) = ETHCALL.staticcall(abi.encode(contractAddr, input1)); + function ethcall(address contractAddr, bytes memory input1) internal returns (bytes memory) { + (bool success, bytes memory data) = ETHCALL.call(abi.encode(contractAddr, input1)); if (!success) { revert PeekerReverted(ETHCALL, data); } @@ -164,9 +163,9 @@ library Suave { return abi.decode(data, (bytes)); } - function extractHint(bytes memory bundleData) internal view returns (bytes memory) { + function extractHint(bytes memory bundleData) internal returns (bytes memory) { require(isConfidential()); - (bool success, bytes memory data) = EXTRACT_HINT.staticcall(abi.encode(bundleData)); + (bool success, bytes memory data) = EXTRACT_HINT.call(abi.encode(bundleData)); if (!success) { revert PeekerReverted(EXTRACT_HINT, data); } @@ -174,8 +173,8 @@ library Suave { return data; } - function fetchDataRecords(uint64 cond, string memory namespace) internal view returns (DataRecord[] memory) { - (bool success, bytes memory data) = FETCH_DATA_RECORDS.staticcall(abi.encode(cond, namespace)); + function fetchDataRecords(uint64 cond, string memory namespace) internal returns (DataRecord[] memory) { + (bool success, bytes memory data) = FETCH_DATA_RECORDS.call(abi.encode(cond, namespace)); if (!success) { revert PeekerReverted(FETCH_DATA_RECORDS, data); } @@ -183,9 +182,9 @@ library Suave { return abi.decode(data, (DataRecord[])); } - function fillMevShareBundle(DataId dataId) internal view returns (bytes memory) { + function fillMevShareBundle(DataId dataId) internal returns (bytes memory) { require(isConfidential()); - (bool success, bytes memory data) = FILL_MEV_SHARE_BUNDLE.staticcall(abi.encode(dataId)); + (bool success, bytes memory data) = FILL_MEV_SHARE_BUNDLE.call(abi.encode(dataId)); if (!success) { revert PeekerReverted(FILL_MEV_SHARE_BUNDLE, data); } @@ -193,8 +192,8 @@ library Suave { return data; } - function newBuilder() internal view returns (string memory) { - (bool success, bytes memory data) = NEW_BUILDER.staticcall(abi.encode()); + function newBuilder() internal returns (string memory) { + (bool success, bytes memory data) = NEW_BUILDER.call(abi.encode()); if (!success) { revert PeekerReverted(NEW_BUILDER, data); } @@ -207,9 +206,9 @@ library Suave { address[] memory allowedPeekers, address[] memory allowedStores, string memory dataType - ) internal view returns (DataRecord memory) { + ) internal returns (DataRecord memory) { (bool success, bytes memory data) = - NEW_DATA_RECORD.staticcall(abi.encode(decryptionCondition, allowedPeekers, allowedStores, dataType)); + NEW_DATA_RECORD.call(abi.encode(decryptionCondition, allowedPeekers, allowedStores, dataType)); if (!success) { revert PeekerReverted(NEW_DATA_RECORD, data); } @@ -219,10 +218,9 @@ library Suave { function signEthTransaction(bytes memory txn, string memory chainId, string memory signingKey) internal - view returns (bytes memory) { - (bool success, bytes memory data) = SIGN_ETH_TRANSACTION.staticcall(abi.encode(txn, chainId, signingKey)); + (bool success, bytes memory data) = SIGN_ETH_TRANSACTION.call(abi.encode(txn, chainId, signingKey)); if (!success) { revert PeekerReverted(SIGN_ETH_TRANSACTION, data); } @@ -230,9 +228,9 @@ library Suave { return abi.decode(data, (bytes)); } - function signMessage(bytes memory digest, string memory signingKey) internal view returns (bytes memory) { + function signMessage(bytes memory digest, string memory signingKey) internal returns (bytes memory) { require(isConfidential()); - (bool success, bytes memory data) = SIGN_MESSAGE.staticcall(abi.encode(digest, signingKey)); + (bool success, bytes memory data) = SIGN_MESSAGE.call(abi.encode(digest, signingKey)); if (!success) { revert PeekerReverted(SIGN_MESSAGE, data); } @@ -240,8 +238,8 @@ library Suave { return abi.decode(data, (bytes)); } - function simulateBundle(bytes memory bundleData) internal view returns (uint64) { - (bool success, bytes memory data) = SIMULATE_BUNDLE.staticcall(abi.encode(bundleData)); + function simulateBundle(bytes memory bundleData) internal returns (uint64) { + (bool success, bytes memory data) = SIMULATE_BUNDLE.call(abi.encode(bundleData)); if (!success) { revert PeekerReverted(SIMULATE_BUNDLE, data); } @@ -251,10 +249,9 @@ library Suave { function simulateTransaction(string memory sessionid, bytes memory txn) internal - view returns (SimulateTransactionResult memory) { - (bool success, bytes memory data) = SIMULATE_TRANSACTION.staticcall(abi.encode(sessionid, txn)); + (bool success, bytes memory data) = SIMULATE_TRANSACTION.call(abi.encode(sessionid, txn)); if (!success) { revert PeekerReverted(SIMULATE_TRANSACTION, data); } @@ -264,11 +261,10 @@ library Suave { function submitBundleJsonRPC(string memory url, string memory method, bytes memory params) internal - view returns (bytes memory) { require(isConfidential()); - (bool success, bytes memory data) = SUBMIT_BUNDLE_JSON_RPC.staticcall(abi.encode(url, method, params)); + (bool success, bytes memory data) = SUBMIT_BUNDLE_JSON_RPC.call(abi.encode(url, method, params)); if (!success) { revert PeekerReverted(SUBMIT_BUNDLE_JSON_RPC, data); } @@ -276,13 +272,9 @@ library Suave { return data; } - function submitEthBlockToRelay(string memory relayUrl, bytes memory builderBid) - internal - view - returns (bytes memory) - { + function submitEthBlockToRelay(string memory relayUrl, bytes memory builderBid) internal returns (bytes memory) { require(isConfidential()); - (bool success, bytes memory data) = SUBMIT_ETH_BLOCK_TO_RELAY.staticcall(abi.encode(relayUrl, builderBid)); + (bool success, bytes memory data) = SUBMIT_ETH_BLOCK_TO_RELAY.call(abi.encode(relayUrl, builderBid)); if (!success) { revert PeekerReverted(SUBMIT_ETH_BLOCK_TO_RELAY, data); } diff --git a/test/Forge.t.sol b/test/Forge.t.sol index 2ab9d13..68f50f3 100644 --- a/test/Forge.t.sol +++ b/test/Forge.t.sol @@ -8,45 +8,25 @@ import "src/suavelib/Suave.sol"; contract TestForge is Test, SuaveEnabled { address[] public addressList = [0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829]; - function testForgeConfidentialStore() public { - //Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); + function testForgeConfidentialStoreFetch() public { + Suave.newDataRecord(0, addressList, addressList, "namespace"); - //console.log("_ RECORD FOUJN?"); + Suave.DataRecord[] memory records = Suave.fetchDataRecords(0, "namespace"); + assertEq(records.length, 1); - //bytes memory value = abi.encode("suave works with forge!"); - //Suave.confidentialStore(record.id, "key1", value); + Suave.newDataRecord(0, addressList, addressList, "namespace"); + Suave.newDataRecord(0, addressList, addressList, "namespace"); - Target target = new Target(); - TargetProxy proxy = new TargetProxy(address(target)); - - address proxyAddr = address(proxy); - proxyAddr.call(abi.encode()); - - console.log(target.count()); - - //bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); - //assertEq(keccak256(found), keccak256(value)); - } - - /* - function testConfidentialReset() public { - Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); - - bytes memory value = abi.encode("suave works with forge!"); - Suave.confidentialStore(record.id, "key1", value); - - bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); - console.logBytes(found); + Suave.DataRecord[] memory records2 = Suave.fetchDataRecords(0, "namespace"); + assertEq(records2.length, 3); resetConfidentialStore(); - bytes memory found2 = Suave.confidentialRetrieve(record.id, "key1"); - assertEq(found2.length, 0); + Suave.DataRecord[] memory records3 = Suave.fetchDataRecords(0, "namespace"); + assertEq(records3.length, 0); } - */ - /* - function testConfidentialReset() public { + function testForgeConfidentialStoreRecordStore() public { Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); bytes memory value = abi.encode("suave works with forge!"); @@ -55,7 +35,6 @@ contract TestForge is Test, SuaveEnabled { bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); assertEq(keccak256(found), keccak256(value)); } - */ function testForgeConfidentialInputs() public { // ensure that the confidential inputs are empty diff --git a/test/forge/ConfidentialStore.t.sol b/test/forge/ConfidentialStore.t.sol index 3ddc8ad..015e5a3 100644 --- a/test/forge/ConfidentialStore.t.sol +++ b/test/forge/ConfidentialStore.t.sol @@ -12,7 +12,7 @@ contract TestConfidentialStore is Test { store = new ConfidentialStore(); } - function testConfidentialStoreNewRecordAndFetch() public { + function testMockConfidentialStoreNewRecordAndFetch() public { // This function tests that we can create new data records and fetch them store.newDataRecord(0, addressList, addressList, "namespace"); store.newDataRecord(0, addressList, addressList, "namespace1"); @@ -36,7 +36,7 @@ contract TestConfidentialStore is Test { assertEq(records.length, 3); } - function testConfidentialStoreStoreRetrieve() public { + function testMockConfidentialStoreStoreRetrieve() public { // This function tests that we can store and retrieve a value from the record Suave.DataRecord memory record = store.newDataRecord(0, addressList, addressList, "namespace"); @@ -46,4 +46,34 @@ contract TestConfidentialStore is Test { bytes memory found = store.confidentialRetrieve(record.id, "key1"); assertEq(keccak256(found), keccak256(value)); } + + function testMockConfidentialStoreReset() public { + // add one record and one stored value + Suave.DataRecord memory record = store.newDataRecord(0, addressList, addressList, "namespace"); + bytes memory value = abi.encodePacked("value"); + store.confidentialStore(record.id, "key1", value); + + bytes memory found = store.confidentialRetrieve(record.id, "key1"); + assertEq(keccak256(found), keccak256(value)); + + // reset the store + store.reset(); + + bytes memory found1 = store.confidentialRetrieve(record.id, "key1"); + assertEq(found1.length, 0); + + Suave.DataRecord[] memory records = store.fetchDataRecords(0, "namespace"); + assertEq(records.length, 0); + + // validate that if we add new records we can reset again + store.newDataRecord(0, addressList, addressList, "namespace"); + + records = store.fetchDataRecords(0, "namespace"); + assertEq(records.length, 1); + + store.reset(); + + records = store.fetchDataRecords(0, "namespace"); + assertEq(records.length, 0); + } } From bf2db7c8beeb473a37cf903864f2f81f85c4c850 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 2 Feb 2024 16:31:03 +0000 Subject: [PATCH 3/6] =?UTF-8?q?Remove=20view=20func=C3=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Transactions.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Transactions.sol b/src/Transactions.sol index fd01200..626a0d3 100644 --- a/src/Transactions.sol +++ b/src/Transactions.sol @@ -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); @@ -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); From 9e438e7f3570bfdf4fb35bcec4809fa0447050cd Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Mon, 5 Feb 2024 10:25:31 +0000 Subject: [PATCH 4/6] Clean PR --- src/Test.sol | 2 +- src/forge/ConfidentialStore.sol | 68 ++++++++---------------- src/forge/ConfidentialStoreConnector.sol | 47 ++++++++++++++++ src/forge/Registry.sol | 9 ++-- test/forge/ConfidentialStore.t.sol | 21 +++++++- 5 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 src/forge/ConfidentialStoreConnector.sol diff --git a/src/Test.sol b/src/Test.sol index 660fba4..86affec 100644 --- a/src/Test.sol +++ b/src/Test.sol @@ -16,7 +16,7 @@ interface ConfidentialStoreI { contract SuaveEnabled is Test { ConfidentialInputsWrapperI constant confInputsWrapper = ConfidentialInputsWrapperI(Suave.CONFIDENTIAL_INPUTS); - ConfidentialStoreI constant confStoreWrapper = ConfidentialStoreI(0x0101010101010101010101010101010101010101); + ConfidentialStoreI constant confStoreWrapper = ConfidentialStoreI(Registry.confidentialStoreAddr); function setUp() public { string[] memory inputs = new string[](3); diff --git a/src/forge/ConfidentialStore.sol b/src/forge/ConfidentialStore.sol index 4933cf3..a3ca440 100644 --- a/src/forge/ConfidentialStore.sol +++ b/src/forge/ConfidentialStore.sol @@ -4,9 +4,12 @@ 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; @@ -34,6 +37,9 @@ contract ConfidentialStore is Test { 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); @@ -47,11 +53,26 @@ contract ConfidentialStore is Test { } function confidentialStore(Suave.DataId dataId, string memory key, bytes memory value) public { - dataRecordsContent[dataId][key] = value; + 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) { - return dataRecordsContent[dataId][key]; + 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 { @@ -61,46 +82,3 @@ contract ConfidentialStore is Test { } } } - -contract ConfidentialStoreWrapper is Test { - fallback() external { - address confidentialStoreAddr = 0x0101010101010101010101010101010101010101; - - address addr = address(this); - bytes4 sig; - - // You can use 'forge selectors list' to retrieve the function signatures - if (addr == Suave.CONFIDENTIAL_STORE) { - sig = 0xd22a3b0b; - } else if (addr == Suave.CONFIDENTIAL_RETRIEVE) { - sig = 0xe3b417bc; - } else if (addr == Suave.FETCH_DATA_RECORDS) { - sig = 0xccb885c4; - } else if (addr == Suave.NEW_DATA_RECORD) { - sig = 0xe3fbcfc3; - } 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 unloop 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) - } - } -} diff --git a/src/forge/ConfidentialStoreConnector.sol b/src/forge/ConfidentialStoreConnector.sol new file mode 100644 index 0000000..3f10fb2 --- /dev/null +++ b/src/forge/ConfidentialStoreConnector.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.8; + +import "../suavelib/Suave.sol"; + +contract ConfidentialStoreConnector { + fallback() external { + address confidentialStoreAddr = 0x0101010101010101010101010101010101010101; + + address addr = address(this); + bytes4 sig; + + // You can use 'forge selectors list' to retrieve the function signatures + if (addr == Suave.CONFIDENTIAL_STORE) { + sig = 0xd22a3b0b; + } else if (addr == Suave.CONFIDENTIAL_RETRIEVE) { + sig = 0xe3b417bc; + } else if (addr == Suave.FETCH_DATA_RECORDS) { + sig = 0xccb885c4; + } else if (addr == Suave.NEW_DATA_RECORD) { + sig = 0xe3fbcfc3; + } 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) + } + } +} diff --git a/src/forge/Registry.sol b/src/forge/Registry.sol index 09fee1f..2101166 100644 --- a/src/forge/Registry.sol +++ b/src/forge/Registry.sol @@ -7,6 +7,7 @@ import "./Connector.sol"; import "./ConfidentialInputs.sol"; import "./SuaveAddrs.sol"; import "./ConfidentialStore.sol"; +import "./ConfidentialStoreConnector.sol"; interface registryVM { function etch(address, bytes calldata) external; @@ -28,10 +29,10 @@ library Registry { deployCodeTo(type(ConfidentialStore).creationCode, confidentialStoreAddr); // enable the confidential inputs wrapper - vm.etch(Suave.CONFIDENTIAL_RETRIEVE, type(ConfidentialStoreWrapper).runtimeCode); - vm.etch(Suave.CONFIDENTIAL_STORE, type(ConfidentialStoreWrapper).runtimeCode); - vm.etch(Suave.NEW_DATA_RECORD, type(ConfidentialStoreWrapper).runtimeCode); - vm.etch(Suave.FETCH_DATA_RECORDS, type(ConfidentialStoreWrapper).runtimeCode); + 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); diff --git a/test/forge/ConfidentialStore.t.sol b/test/forge/ConfidentialStore.t.sol index 015e5a3..8702867 100644 --- a/test/forge/ConfidentialStore.t.sol +++ b/test/forge/ConfidentialStore.t.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import "src/forge/ConfidentialStore.sol"; -contract TestConfidentialStore is Test { +contract TestMockConfidentialStore is Test { ConfidentialStore store; - address[] public addressList = [0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829]; + address[] public addressList = [Suave.ANYALLOWED]; function setUp() public { store = new ConfidentialStore(); @@ -47,6 +47,23 @@ contract TestConfidentialStore is Test { assertEq(keccak256(found), keccak256(value)); } + function testMockConfidentialStoreLocalAllowedAddress() public { + // This function tests that we can store and retrieve a value from the record + address[] memory allowed = new address[](1); + allowed[0] = address(this); + + Suave.DataRecord memory record = store.newDataRecord(0, allowed, allowed, "namespace"); + + bytes memory value = abi.encodePacked("value"); + store.confidentialStore(record.id, "key1", value); + + // test that another address cannot store + vm.startPrank(0x0000000000000000000000000000000000000000); + vm.expectRevert(); + store.confidentialStore(record.id, "key1", value); + vm.stopPrank(); + } + function testMockConfidentialStoreReset() public { // add one record and one stored value Suave.DataRecord memory record = store.newDataRecord(0, addressList, addressList, "namespace"); From e3b080bce60b5790c2795549f8f1c3bda3ad1323 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Mon, 5 Feb 2024 10:30:58 +0000 Subject: [PATCH 5/6] Fix example --- test/forge/ConfidentialStore.t.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/forge/ConfidentialStore.t.sol b/test/forge/ConfidentialStore.t.sol index 8702867..6af080f 100644 --- a/test/forge/ConfidentialStore.t.sol +++ b/test/forge/ConfidentialStore.t.sol @@ -76,8 +76,9 @@ contract TestMockConfidentialStore is Test { // reset the store store.reset(); - bytes memory found1 = store.confidentialRetrieve(record.id, "key1"); - assertEq(found1.length, 0); + // it reverts because it cannot find the metadata of 'record'. + vm.expectRevert(); + store.confidentialRetrieve(record.id, "key1"); Suave.DataRecord[] memory records = store.fetchDataRecords(0, "namespace"); assertEq(records.length, 0); From bf8ccc94f9baac5b8fbbc9ac23201f17fe74b384 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Mon, 5 Feb 2024 13:36:15 +0000 Subject: [PATCH 6/6] =?UTF-8?q?Add=20signatures=20in=20a=20better=20way?= =?UTF-8?q?=C2=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forge/ConfidentialStoreConnector.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/forge/ConfidentialStoreConnector.sol b/src/forge/ConfidentialStoreConnector.sol index 3f10fb2..87a355e 100644 --- a/src/forge/ConfidentialStoreConnector.sol +++ b/src/forge/ConfidentialStoreConnector.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.8; +import "./ConfidentialStore.sol"; import "../suavelib/Suave.sol"; contract ConfidentialStoreConnector { @@ -10,15 +11,14 @@ contract ConfidentialStoreConnector { address addr = address(this); bytes4 sig; - // You can use 'forge selectors list' to retrieve the function signatures if (addr == Suave.CONFIDENTIAL_STORE) { - sig = 0xd22a3b0b; + sig = ConfidentialStore.confidentialStore.selector; } else if (addr == Suave.CONFIDENTIAL_RETRIEVE) { - sig = 0xe3b417bc; + sig = ConfidentialStore.confidentialRetrieve.selector; } else if (addr == Suave.FETCH_DATA_RECORDS) { - sig = 0xccb885c4; + sig = ConfidentialStore.fetchDataRecords.selector; } else if (addr == Suave.NEW_DATA_RECORD) { - sig = 0xe3fbcfc3; + sig = ConfidentialStore.newDataRecord.selector; } else { revert("function signature not found in the confidential store"); }