Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
DataContractTest:testErrorBadAddressRead(address) (runs: 1024, μ: 8865, ~: 8865)
DataContractTest:testNewAddressFuzzData(bytes) (runs: 1024, μ: 86825, ~: 82800)
DataContractTest:testNewAddressFuzzDataDifferent(bytes,bytes) (runs: 1023, μ: 87936, ~: 83281)
DataContractTest:testRoundFuzz(bytes,bytes) (runs: 1024, μ: 46675, ~: 44341)
DataContractTest:testRoundSlice(bytes,uint16,uint16) (runs: 114, μ: 48835, ~: 49038)
DataContractTest:testRoundSliceError(bytes,uint16,uint16) (runs: 910, μ: 48306, ~: 46529)
DataContractTest:testSameReads(bytes) (runs: 1024, μ: 46392, ~: 44315)
DataContractTest:testZeroPrefix(bytes) (runs: 1024, μ: 42023, ~: 40036)
DataContractTest:testZoltu() (gas: 79418)
DataContractTest:testErrorBadAddressRead(address) (runs: 1024, μ: 8771, ~: 8771)
DataContractTest:testNewAddressFuzzData(bytes) (runs: 1024, μ: 85287, ~: 82448)
DataContractTest:testNewAddressFuzzDataDifferent(bytes,bytes) (runs: 1024, μ: 86318, ~: 82982)
DataContractTest:testRoundFuzz(bytes,bytes) (runs: 1024, μ: 45487, ~: 43907)
DataContractTest:testRoundSlice(bytes,uint16,uint16) (runs: 1024, μ: 46636, ~: 44212)
DataContractTest:testRoundSliceError(bytes,uint16,uint16) (runs: 1024, μ: 47523, ~: 46303)
DataContractTest:testSameReads(bytes) (runs: 1024, μ: 45381, ~: 43952)
DataContractTest:testZeroPrefix(bytes) (runs: 1024, μ: 41309, ~: 39893)
DataContractTest:testZoltu() (gas: 78935)
DataContractTest:testZoltuBadZoltu(bytes) (runs: 1024, μ: 1040428879, ~: 1040429085)
DataContractTest:testZoltuNoZoltu(bytes) (runs: 1024, μ: 8598, ~: 8587)
59 changes: 38 additions & 21 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"lib/forge-std": {
"rev": "b8f065fda83b8cd94a6b2fec8fcd911dc3b444fd"
"rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6"
},
"lib/rain.solmem": {
"rev": "f28da2f09d42d154783cb53c138a7ef2f3a9eb4a"
"rev": "2e47e41af85a711d837b2518656db0efdae814c2"
}
}
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 57 files
+14 −31 .github/workflows/ci.yml
+1 −1 .github/workflows/sync.yml
+2 −2 CONTRIBUTING.md
+11 −9 README.md
+3 −12 foundry.toml
+2 −2 package.json
+2 −12 scripts/vm.py
+2 −2 src/Base.sol
+1 −1 src/Config.sol
+2 −2 src/LibVariable.sol
+2 −2 src/Script.sol
+28 −13 src/StdAssertions.sol
+8 −5 src/StdChains.sol
+9 −13 src/StdCheats.sol
+24 −4 src/StdConfig.sol
+2 −2 src/StdConstants.sol
+2 −2 src/StdError.sol
+2 −4 src/StdInvariant.sol
+4 −12 src/StdJson.sol
+2 −2 src/StdMath.sol
+11 −9 src/StdStorage.sol
+2 −2 src/StdStyle.sol
+4 −12 src/StdToml.sol
+5 −13 src/StdUtils.sol
+2 −4 src/Test.sol
+26 −41 src/Vm.sol
+10 −19 src/console.sol
+2 −2 src/console2.sol
+2 −2 src/interfaces/IERC1155.sol
+2 −2 src/interfaces/IERC165.sol
+2 −2 src/interfaces/IERC20.sol
+2 −2 src/interfaces/IERC4626.sol
+2 −2 src/interfaces/IERC6909.sol
+2 −2 src/interfaces/IERC721.sol
+4 −10 src/interfaces/IERC7540.sol
+2 −2 src/interfaces/IERC7575.sol
+3 −8 src/interfaces/IMulticall3.sol
+691 −1,380 src/safeconsole.sol
+2 −2 test/CommonBase.t.sol
+34 −5 test/Config.t.sol
+19 −1 test/LibVariable.t.sol
+2 −2 test/StdAssertions.t.sol
+24 −24 test/StdChains.t.sol
+19 −20 test/StdCheats.t.sol
+2 −2 test/StdConstants.t.sol
+3 −4 test/StdError.t.sol
+2 −2 test/StdJson.t.sol
+6 −6 test/StdMath.t.sol
+24 −27 test/StdStorage.t.sol
+2 −2 test/StdStyle.t.sol
+2 −2 test/StdToml.t.sol
+13 −13 test/StdUtils.t.sol
+3 −3 test/Vm.t.sol
+2 −4 test/compilation/CompilationScript.sol
+2 −4 test/compilation/CompilationScriptBase.sol
+2 −4 test/compilation/CompilationTest.sol
+2 −4 test/compilation/CompilationTestBase.sol
71 changes: 46 additions & 25 deletions src/lib/LibDataContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pragma solidity ^0.8.25;
import {LibPointer, Pointer} from "../../lib/rain.solmem/src/lib/LibPointer.sol";
import {WriteError, ReadError} from "../error/ErrDataContract.sol";

/// @dev SSTORE2 Verbatim reference
/// @dev SSTORE2 Verbatim original reference
/// https://github.com/0xsequence/sstore2/blob/master/contracts/utils/Bytecode.sol#L15
///
/// 0x00 0x63 0x63XXXXXX PUSH4 _code.length size
Expand All @@ -18,6 +18,9 @@ import {WriteError, ReadError} from "../error/ErrDataContract.sol";
/// 0x06 0xf3 0xf3 RETURN
/// <CODE>
///
/// The assembly below is a modified version of this original reference according
/// to the notes following.
///
/// However note that 00 is also prepended (although docs say append) so there's
/// an additional byte that isn't described above.
/// https://github.com/0xsequence/sstore2/blob/master/contracts/SSTORE2.sol#L25
Expand All @@ -30,12 +33,27 @@ import {WriteError, ReadError} from "../error/ErrDataContract.sol";
/// This also changes the 0x600e to 0x600c as we've reduced prefix size by 2
/// relative to reference implementation.
/// https://github.com/0xsequence/sstore2/pull/5/files
///
/// The final modified bytecode is therefore:
/// 0x61 0x61XXXX PUSH2 _code.length size
/// 0x80 0x80 DUP1 size size
/// 0x60 0x600c PUSH1 12 12 size size
/// 0x60 0x6000 PUSH1 00 0 12 size size
/// 0x39 0x39 CODECOPY size
/// 0x60 0x6000 PUSH1 00 0 size
/// 0xf3 0xf3 RETURN
/// 0x00 0x00 <extra byte prepended by SSTORE2>
/// <CODE>
uint256 constant BASE_PREFIX = 0x61_0000_80_600C_6000_39_6000_F3_00_00000000000000000000000000000000000000;

/// @dev Length of the prefix that converts in memory data to a deployable
/// contract.
uint256 constant PREFIX_BYTES_LENGTH = 13;

/// @dev Zoltu deterministic deployment proxy address.
/// https://github.com/Zoltu/deterministic-deployment-proxy?tab=readme-ov-file#proxy-address
address constant ZOLTU_PROXY_ADDRESS = 0x7A0D94F55792C434d74a40883C6ed8545E406D12;

/// A container is a region of memory that is directly deployable with `create`,
/// without length prefixes or other Solidity type trappings. Where the length is
/// needed, such as in `write` it can be read as bytes `[1,2]` from the prefix.
Expand Down Expand Up @@ -116,16 +134,17 @@ library LibDataContract {
address pointer;
uint256 prefixLength = PREFIX_BYTES_LENGTH;
assembly ("memory-safe") {
pointer :=
create(
0,
container,
add(
prefixLength,
// Read length out of prefix.
and(0xFFFF, shr(232, mload(container)))
)
pointer := create(
0,
container,
add(
prefixLength,
// Read length out of prefix.
// Sub 1 as length stored is +1 to include the 0x00 prefix
// byte.
sub(and(0xFFFF, shr(232, mload(container))), 1)
)
)
}
// Zero address means create failed.
if (pointer == address(0)) revert WriteError();
Expand All @@ -137,27 +156,29 @@ library LibDataContract {
/// will be the same on all networks and for all callers for the same data.
/// https://github.com/Zoltu/deterministic-deployment-proxy
function writeZoltu(DataContractMemoryContainer container) internal returns (address deployedAddress) {
address zoltu = ZOLTU_PROXY_ADDRESS;
uint256 prefixLength = PREFIX_BYTES_LENGTH;
bool success;
assembly ("memory-safe") {
mstore(0, 0)
success :=
call(
gas(),
0x7A0D94F55792C434d74a40883C6ed8545E406D12,
0,
container,
add(
prefixLength,
// Read length out of prefix.
and(0xFFFF, shr(232, mload(container)))
),
12,
20
)
success := call(
gas(),
zoltu,
0,
container,
add(
prefixLength,
// Read length out of prefix.
// Sub 1 as length stored is +1 to include the 0x00 prefix
// byte.
sub(and(0xFFFF, shr(232, mload(container))), 1)
),
12,
20
)
deployedAddress := mload(0)
}
if (!success) revert WriteError();
if (deployedAddress == address(0) || !success) revert WriteError();
}

/// Reads data back from a previously deployed container.
Expand Down
44 changes: 36 additions & 8 deletions test/lib/LibDataContract.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import {LibMemCpy} from "rain.solmem/lib/LibMemCpy.sol";
import {LibBytes} from "rain.solmem/lib/LibBytes.sol";

import {
LibPointer, Pointer, DataContractMemoryContainer, LibDataContract, ReadError
LibPointer,
Pointer,
DataContractMemoryContainer,
LibDataContract,
ReadError,
WriteError,
ZOLTU_PROXY_ADDRESS
} from "src/lib/LibDataContract.sol";

/// @title DataContractTest
Expand All @@ -21,14 +27,16 @@ contract DataContractTest is Test {
return LibDataContract.read(datacontract);
}

function readSliceExternal(address datacontract, uint16 start, uint16 length)
external
view
returns (bytes memory)
{
function readSliceExternal(address datacontract, uint16 start, uint16 length) external view returns (bytes memory) {
return LibDataContract.readSlice(datacontract, start, length);
}

function writeZoltuExternal(bytes memory data) external returns (address) {
(DataContractMemoryContainer container, Pointer pointer) = LibDataContract.newContainer(data.length);
LibMemCpy.unsafeCopyBytesTo(data.dataPointer(), pointer, data.length);
return LibDataContract.writeZoltu(container);
}

/// Writing any data to a contract then reading it back without corrupting
/// memory or the data itself.
function testRoundFuzz(bytes memory data, bytes memory garbage) public {
Expand Down Expand Up @@ -171,14 +179,34 @@ contract DataContractTest is Test {

address datacontractAlpha = LibDataContract.writeZoltu(container);

assertEq(datacontractAlpha, 0x7B5220368D7460A84bCFCCB0616f77E61e5302e2);
assertEq(datacontractAlpha, 0x1Cf89F16784b780E549105B04e80D5196E13C4Af);
assertEq(keccak256(data), keccak256(LibDataContract.read(datacontractAlpha)));

vm.createSelectFork(vm.envString("CI_FORK_AVALANCHE_RPC_URL"));

address datacontractBeta = LibDataContract.writeZoltu(container);

assertEq(datacontractBeta, 0x7B5220368D7460A84bCFCCB0616f77E61e5302e2);
assertEq(datacontractBeta, 0x1Cf89F16784b780E549105B04e80D5196E13C4Af);
assertEq(keccak256(data), keccak256(LibDataContract.read(datacontractBeta)));
}

/// Check that if we use zoltu without the zoltu proxy existing that we
/// revert.
function testZoltuNoZoltu(bytes memory data) external {
vm.assume(ZOLTU_PROXY_ADDRESS.code.length == 0);
vm.expectRevert(abi.encodeWithSelector(WriteError.selector));
this.writeZoltuExternal(data);
}

/// Check that if zoltu exists but returns not success we revert.
function testZoltuBadZoltu(bytes memory data) external {
vm.assume(ZOLTU_PROXY_ADDRESS.code.length == 0);
vm.etch(
ZOLTU_PROXY_ADDRESS,
// revert opcode.
hex"fd"
);
vm.expectRevert(abi.encodeWithSelector(WriteError.selector));
this.writeZoltuExternal(data);
}
}