diff --git a/.changeset/big-tables-bake.md b/.changeset/big-tables-bake.md new file mode 100644 index 0000000000000..e495a0b3a84f7 --- /dev/null +++ b/.changeset/big-tables-bake.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/core-utils': patch +--- + +Add encoding and hashing functions for bedrock diff --git a/.changeset/strange-pillows-walk.md b/.changeset/strange-pillows-walk.md new file mode 100644 index 0000000000000..cbb42b82fa9da --- /dev/null +++ b/.changeset/strange-pillows-walk.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/contracts-bedrock': patch +'@eth-optimism/contracts-periphery': patch +--- + +Update forge-std diff --git a/.circleci/config.yml b/.circleci/config.yml index 7c0db8a783cbd..646e21711fe1f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -107,7 +107,7 @@ jobs: contracts-bedrock-tests: docker: - image: ethereumoptimism/ci-builder:latest - resource_class: medium + resource_class: large steps: - restore_cache: keys: @@ -135,11 +135,15 @@ jobs: name: test command: yarn test working_directory: packages/contracts-bedrock + environment: + FOUNDRY_PROFILE: ci - run: name: gas snapshot command: | forge --version forge snapshot --check || exit 0 + environment: + FOUNDRY_PROFILE: ci working_directory: packages/contracts-bedrock - run: name: storage snapshot diff --git a/packages/contracts-bedrock/.gas-snapshot b/packages/contracts-bedrock/.gas-snapshot index 5a0aad40370c6..90abdfbc4565b 100644 --- a/packages/contracts-bedrock/.gas-snapshot +++ b/packages/contracts-bedrock/.gas-snapshot @@ -10,7 +10,6 @@ GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 74944) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 35773) DeployerWhitelist_Test:test_owner() (gas: 7591) DeployerWhitelist_Test:test_storageSlots() (gas: 33427) -Encoding_Test:test_encodeDepositTransaction() (gas: 64610) GasPriceOracle_Test:test_baseFee() (gas: 8392) GasPriceOracle_Test:test_gasPrice() (gas: 8381) GasPriceOracle_Test:test_l1BaseFee() (gas: 10648) @@ -24,8 +23,7 @@ GasPriceOracle_Test:test_setL1BaseFeeReverts() (gas: 11739) GasPriceOracle_Test:test_setOverhead() (gas: 36789) GasPriceOracle_Test:test_setScalar() (gas: 36796) GasPriceOracle_Test:test_storageLayout() (gas: 86705) -Hashing_Test:test_hashDepositSource() (gas: 673) -Hashing_Test:test_hashDepositTransaction() (gas: 39129) +Hashing_Test:test_hashDepositSource() (gas: 628) L1BlockTest:test_basefee() (gas: 7575) L1BlockTest:test_hash() (gas: 7552) L1BlockTest:test_number() (gas: 7629) @@ -50,18 +48,18 @@ L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 1489673) L1CrossDomainMessenger_Test:test_L1MessengerUnpause() (gas: 40852) L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 24313) L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 86364) -L1StandardBridge_Test:test_depositERC20() (gas: 578548) -L1StandardBridge_Test:test_depositERC20To() (gas: 580752) +L1StandardBridge_Test:test_depositERC20() (gas: 578572) +L1StandardBridge_Test:test_depositERC20To() (gas: 580779) L1StandardBridge_Test:test_depositETH() (gas: 372464) L1StandardBridge_Test:test_depositETHTo() (gas: 329608) -L1StandardBridge_Test:test_finalizeBridgeERC20FailSendBack() (gas: 681009) -L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 490749) +L1StandardBridge_Test:test_finalizeBridgeERC20FailSendBack() (gas: 681028) +L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 490771) L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 64276) L1StandardBridge_Test:test_initialize() (gas: 26334) L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 22376) L1StandardBridge_Test:test_onlyEOADepositETH() (gas: 40940) -L1StandardBridge_Test:test_onlyL2BridgeFinalizeERC20Withdrawal() (gas: 36264) -L1StandardBridge_Test:test_onlyPortalFinalizeERC20Withdrawal() (gas: 35569) +L1StandardBridge_Test:test_onlyL2BridgeFinalizeERC20Withdrawal() (gas: 36268) +L1StandardBridge_Test:test_onlyPortalFinalizeERC20Withdrawal() (gas: 35573) L1StandardBridge_Test:test_receive() (gas: 519287) L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10845) L2CrossDomainMessenger_Test:test_L1MessengerRelayMessageRevertsOnReentrancy() (gas: 171927) @@ -101,16 +99,16 @@ L2OutputOracleUpgradeable_Test:test_upgrading() (gas: 230843) L2StandardBridge_Test:test_ERC20BridgeFailed_whenLocalTokenIsBridge() (gas: 132769) L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21622) L2StandardBridge_Test:test_finalizeBridgeERC20FailSendBack() (gas: 498579) -L2StandardBridge_Test:test_finalizeDeposit() (gas: 93113) +L2StandardBridge_Test:test_finalizeDeposit() (gas: 93128) L2StandardBridge_Test:test_finalizeDeposit_failsToCompleteOutboundTransfer() (gas: 139784) L2StandardBridge_Test:test_initialize() (gas: 14823) L2StandardBridge_Test:test_receive() (gas: 136330) -L2StandardBridge_Test:test_withdraw() (gas: 352474) -L2StandardBridge_Test:test_withdrawTo() (gas: 353229) -L2StandardBridge_Test:test_withdraw_onlyEOA() (gas: 251979) +L2StandardBridge_Test:test_withdraw() (gas: 352486) +L2StandardBridge_Test:test_withdrawTo() (gas: 353241) +L2StandardBridge_Test:test_withdraw_onlyEOA() (gas: 251991) L2ToL1MessagePasserTest:test_burn() (gas: 112089) L2ToL1MessagePasserTest:test_initiateWithdrawal_fromContract() (gas: 68037) -L2ToL1MessagePasserTest:test_initiateWithdrawal_fromEOA() (gas: 74928) +L2ToL1MessagePasserTest:test_initiateWithdrawal_fromEOA() (gas: 74952) LegacyERC20ETH_Test:test_approve() (gas: 10796) LegacyERC20ETH_Test:test_burn() (gas: 10681) LegacyERC20ETH_Test:test_crossDomain() (gas: 10577) @@ -121,13 +119,13 @@ LegacyERC20ETH_Test:test_mint() (gas: 10627) LegacyERC20ETH_Test:test_transfer() (gas: 10829) LegacyERC20ETH_Test:test_transferFrom() (gas: 13008) OptimismMintableERC20_Test:test_bridge() (gas: 9828) -OptimismMintableERC20_Test:test_burn() (gas: 52773) -OptimismMintableERC20_Test:test_burnRevertsFromNotBridge() (gas: 13219) +OptimismMintableERC20_Test:test_burn() (gas: 52788) +OptimismMintableERC20_Test:test_burnRevertsFromNotBridge() (gas: 13228) OptimismMintableERC20_Test:test_erc165_supportsInterface() (gas: 7828) OptimismMintableERC20_Test:test_l1Token() (gas: 9824) OptimismMintableERC20_Test:test_l2Bridge() (gas: 9746) -OptimismMintableERC20_Test:test_mint() (gas: 65754) -OptimismMintableERC20_Test:test_mintRevertsFromNotBridge() (gas: 13243) +OptimismMintableERC20_Test:test_mint() (gas: 65763) +OptimismMintableERC20_Test:test_mintRevertsFromNotBridge() (gas: 13252) OptimismMintableERC20_Test:test_remoteToken() (gas: 9740) OptimismMintableTokenFactory_Test:test_bridge() (gas: 7663) OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1113127) @@ -140,16 +138,16 @@ OptimismPortalUpgradeable_Test:test_upgrading() (gas: 230843) OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 17341) OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 14215) OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 127503) -OptimismPortal_Test:test_cannotFinalizeRecentWithdrawal() (gas: 24754) +OptimismPortal_Test:test_cannotFinalizeRecentWithdrawal() (gas: 24757) OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 76654) -OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 77108) +OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 77131) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 76659) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA() (gas: 76980) OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 83680) OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 75845) OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 83384) OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 84132) -OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 37266) +OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 37269) OptimismPortal_Test:test_isBlockFinalized() (gas: 113725) OptimismPortal_Test:test_simple_isBlockFinalized() (gas: 26652) Proxy_Test:test_clashingFunctionSignatures() (gas: 101427) @@ -221,12 +219,12 @@ RLPReader_Test:test_readList_nonOptimalLongLengthArray1() (gas: 4233) RLPReader_Test:test_readList_nonOptimalLongLengthArray2() (gas: 4256) RLPReader_Test:test_readList_notLongEnough() (gas: 3874) RLPReader_Test:test_readList_shortListMax1() (gas: 40662) -RLPReader_Test:test_readList_stringList() (gas: 16796) +RLPReader_Test:test_readList_stringList() (gas: 16844) RLPReader_Test:test_readString_emptyString() (gas: 1719) -RLPReader_Test:test_readString_longString() (gas: 3076) +RLPReader_Test:test_readString_longString() (gas: 3092) RLPReader_Test:test_readString_longString2() (gas: 18524) -RLPReader_Test:test_readString_shortString() (gas: 2280) -RLPReader_Test:test_readString_shortString2() (gas: 2775) +RLPReader_Test:test_readString_shortString() (gas: 2296) +RLPReader_Test:test_readString_shortString2() (gas: 2791) RLPReader_Test:test_readUint256_mediumInt1() (gas: 1235) RLPReader_Test:test_readUint256_mediumInt2() (gas: 1237) RLPReader_Test:test_readUint256_mediumInt3() (gas: 1238) @@ -235,31 +233,31 @@ RLPReader_Test:test_readUint256_smallInt2() (gas: 1151) RLPReader_Test:test_readUint256_smallInt3() (gas: 1127) RLPReader_Test:test_readUint256_smallInt4() (gas: 1128) RLPReader_Test:test_readUint256_zero() (gas: 1258) -RLPWriter_Test:test_writeList_dictTest1() (gas: 37148) -RLPWriter_Test:test_writeList_empty() (gas: 1751) -RLPWriter_Test:test_writeList_listoflists() (gas: 10969) -RLPWriter_Test:test_writeList_listoflists2() (gas: 16779) -RLPWriter_Test:test_writeList_longlist1() (gas: 40730) -RLPWriter_Test:test_writeList_longlist2() (gas: 283278) -RLPWriter_Test:test_writeList_multiList() (gas: 22660) -RLPWriter_Test:test_writeList_shortListMax1() (gas: 36985) -RLPWriter_Test:test_writeList_stringList() (gas: 10786) +RLPWriter_Test:test_writeList_dictTest1() (gas: 37356) +RLPWriter_Test:test_writeList_empty() (gas: 1759) +RLPWriter_Test:test_writeList_listoflists() (gas: 11025) +RLPWriter_Test:test_writeList_listoflists2() (gas: 16867) +RLPWriter_Test:test_writeList_longlist1() (gas: 40850) +RLPWriter_Test:test_writeList_longlist2() (gas: 283846) +RLPWriter_Test:test_writeList_multiList() (gas: 22708) +RLPWriter_Test:test_writeList_shortListMax1() (gas: 37177) +RLPWriter_Test:test_writeList_stringList() (gas: 10850) RLPWriter_Test:test_writeString_bytestring00() (gas: 1022) RLPWriter_Test:test_writeString_bytestring01() (gas: 978) RLPWriter_Test:test_writeString_bytestring7f() (gas: 1002) -RLPWriter_Test:test_writeString_empty() (gas: 1704) -RLPWriter_Test:test_writeString_longstring() (gas: 17180) -RLPWriter_Test:test_writeString_longstring2() (gas: 261864) -RLPWriter_Test:test_writeString_shortstring() (gas: 2544) -RLPWriter_Test:test_writeString_shortstring2() (gas: 15563) -RLPWriter_Test:test_writeUint_mediumint() (gas: 8430) -RLPWriter_Test:test_writeUint_mediumint2() (gas: 8780) -RLPWriter_Test:test_writeUint_mediumint3() (gas: 9189) +RLPWriter_Test:test_writeString_empty() (gas: 1712) +RLPWriter_Test:test_writeString_longstring() (gas: 17196) +RLPWriter_Test:test_writeString_longstring2() (gas: 261872) +RLPWriter_Test:test_writeString_shortstring() (gas: 2560) +RLPWriter_Test:test_writeString_shortstring2() (gas: 15579) +RLPWriter_Test:test_writeUint_mediumint() (gas: 8446) +RLPWriter_Test:test_writeUint_mediumint2() (gas: 8796) +RLPWriter_Test:test_writeUint_mediumint3() (gas: 9205) RLPWriter_Test:test_writeUint_smallint() (gas: 7350) RLPWriter_Test:test_writeUint_smallint2() (gas: 7352) RLPWriter_Test:test_writeUint_smallint3() (gas: 7372) RLPWriter_Test:test_writeUint_smallint4() (gas: 7351) -RLPWriter_Test:test_writeUint_zero() (gas: 7798) +RLPWriter_Test:test_writeUint_zero() (gas: 7806) ResourceMetering_Test:test_initialResourceParams() (gas: 8964) ResourceMetering_Test:test_updateNoGasDelta() (gas: 2008317) ResourceMetering_Test:test_updateOneEmptyBlock() (gas: 18171) @@ -268,8 +266,8 @@ ResourceMetering_Test:test_updateTenEmptyBlocks() (gas: 20571) ResourceMetering_Test:test_updateTwoEmptyBlocks() (gas: 20594) ResourceMetering_Test:test_useMaxSucceeds() (gas: 8017087) ResourceMetering_Test:test_useMoreThanMaxReverts() (gas: 16047) -Semver_Test:test_behindProxy() (gas: 506870) -Semver_Test:test_version() (gas: 9451) +Semver_Test:test_behindProxy() (gas: 506894) +Semver_Test:test_version() (gas: 9475) SequencerFeeVault_Test:test_constructor() (gas: 7678) SequencerFeeVault_Test:test_minWithdrawalAmount() (gas: 5440) SequencerFeeVault_Test:test_receive() (gas: 17338) diff --git a/packages/contracts-bedrock/contracts/test/CommonTest.t.sol b/packages/contracts-bedrock/contracts/test/CommonTest.t.sol index d13d17fa74a46..b42b0321bbb78 100644 --- a/packages/contracts-bedrock/contracts/test/CommonTest.t.sol +++ b/packages/contracts-bedrock/contracts/test/CommonTest.t.sol @@ -1,4 +1,4 @@ -//SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT pragma solidity 0.8.10; /* Testing utilities */ @@ -10,7 +10,6 @@ import { L2StandardBridge } from "../L2/L2StandardBridge.sol"; import { OptimismMintableERC20Factory } from "../universal/OptimismMintableERC20Factory.sol"; import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol"; -import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; @@ -23,6 +22,7 @@ import { ResolvedDelegateProxy } from "../legacy/ResolvedDelegateProxy.sol"; import { AddressManager } from "../legacy/AddressManager.sol"; import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol"; import { IL1ChugSplashDeployer } from "../legacy/L1ChugSplashProxy.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; contract CommonTest is Test { address alice = address(128); @@ -44,6 +44,24 @@ contract CommonTest is Test { bytes opaqueData ); + FFIInterface ffi; + + function _setUp() public { + // Give alice and bob some ETH + vm.deal(alice, 1 << 16); + vm.deal(bob, 1 << 16); + vm.deal(multisig, 1 << 16); + + vm.label(alice, "alice"); + vm.label(bob, "bob"); + vm.label(multisig, "multisig"); + + // Make sure we have a non-zero base fee + vm.fee(1000000000); + + ffi = new FFIInterface(); + } + function emitTransactionDeposited( address _from, address _to, @@ -61,19 +79,6 @@ contract CommonTest is Test { ); } - function _setUp() public { - // Give alice and bob some ETH - vm.deal(alice, 1 << 16); - vm.deal(bob, 1 << 16); - vm.deal(multisig, 1 << 16); - - vm.label(alice, "alice"); - vm.label(bob, "bob"); - vm.label(multisig, "multisig"); - - // Make sure we have a non-zero base fee - vm.fee(1000000000); - } } contract L2OutputOracle_Initializer is CommonTest { @@ -81,6 +86,9 @@ contract L2OutputOracle_Initializer is CommonTest { L2OutputOracle oracle; L2OutputOracle oracleImpl; + L2ToL1MessagePasser messagePasser = + L2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER)); + // Constructor arguments address proposer = 0x000000000000000000000000000000000000AbBa; address owner = 0x000000000000000000000000000000000000ACDC; @@ -131,6 +139,15 @@ contract L2OutputOracle_Initializer is CommonTest { ) ); oracle = L2OutputOracle(address(proxy)); + vm.label(address(oracle), "L2OutputOracle"); + + // Set the L2ToL1MessagePasser at the correct address + vm.etch( + Predeploys.L2_TO_L1_MESSAGE_PASSER, + address(new L2ToL1MessagePasser()).code + ); + + vm.label(Predeploys.L2_TO_L1_MESSAGE_PASSER, "L2ToL1MessagePasser"); } } @@ -141,6 +158,7 @@ contract Portal_Initializer is L2OutputOracle_Initializer { function setUp() public virtual override { L2OutputOracle_Initializer.setUp(); + opImpl = new OptimismPortal(oracle, 7 days); Proxy proxy = new Proxy(multisig); vm.prank(multisig); @@ -158,8 +176,6 @@ contract Messenger_Initializer is L2OutputOracle_Initializer { L1CrossDomainMessenger L1Messenger; L2CrossDomainMessenger L2Messenger = L2CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER); - L2ToL1MessagePasser messagePasser = - L2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER)); event SentMessage( address indexed target, @@ -225,18 +241,11 @@ contract Messenger_Initializer is L2OutputOracle_Initializer { L2Messenger.initialize(address(L1Messenger)); - // Set the L2ToL1MessagePasser at the correct address - vm.etch( - Predeploys.L2_TO_L1_MESSAGE_PASSER, - address(new L2ToL1MessagePasser()).code - ); - // Label addresses vm.label(address(addressManager), "AddressManager"); vm.label(address(L1MessengerImpl), "L1CrossDomainMessenger_Impl"); vm.label(address(L1Messenger), "L1CrossDomainMessenger_Proxy"); vm.label(Predeploys.LEGACY_ERC20_ETH, "LegacyERC20ETH"); - vm.label(Predeploys.L2_TO_L1_MESSAGE_PASSER, "L2ToL1MessagePasser"); vm.label(Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L2CrossDomainMessenger"); vm.label( @@ -442,6 +451,164 @@ contract Bridge_Initializer is Messenger_Initializer { } } +contract FFIInterface is Test { + function getFinalizeWithdrawalTransactionInputs( + uint256 _nonce, + address _sender, + address _target, + uint64 _value, + uint256 _gasLimit, + bytes memory _data + ) external returns (bytes32, bytes32, bytes32, bytes32, bytes memory) { + string[] memory cmds = new string[](9); + cmds[0] = "node"; + cmds[1] = "dist/scripts/differential-testing.js"; + cmds[2] = "getFinalizeWithdrawalTransactionInputs"; + cmds[3] = vm.toString(_nonce); + cmds[4] = vm.toString(_sender); + cmds[5] = vm.toString(_target); + cmds[6] = vm.toString(_value); + cmds[7] = vm.toString(_gasLimit); + cmds[8] = vm.toString(_data); + + bytes memory result = vm.ffi(cmds); + ( + bytes32 stateRoot, + bytes32 storageRoot, + bytes32 outputRoot, + bytes32 withdrawalHash, + bytes memory withdrawalProof + ) = abi.decode(result, (bytes32, bytes32, bytes32, bytes32, bytes)); + + return (stateRoot, storageRoot, outputRoot, withdrawalHash, withdrawalProof); + } + + function hashCrossDomainMessage( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) external returns (bytes32) { + string[] memory cmds = new string[](9); + cmds[0] = "node"; + cmds[1] = "dist/scripts/differential-testing.js"; + cmds[2] = "hashCrossDomainMessage"; + cmds[3] = vm.toString(_nonce); + cmds[4] = vm.toString(_sender); + cmds[5] = vm.toString(_target); + cmds[6] = vm.toString(_value); + cmds[7] = vm.toString(_gasLimit); + cmds[8] = vm.toString(_data); + + bytes memory result = vm.ffi(cmds); + return abi.decode(result, (bytes32)); + } + + function hashWithdrawal( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) external returns (bytes32) { + string[] memory cmds = new string[](9); + cmds[0] = "node"; + cmds[1] = "dist/scripts/differential-testing.js"; + cmds[2] = "hashWithdrawal"; + cmds[3] = vm.toString(_nonce); + cmds[4] = vm.toString(_sender); + cmds[5] = vm.toString(_target); + cmds[6] = vm.toString(_value); + cmds[7] = vm.toString(_gasLimit); + cmds[8] = vm.toString(_data); + + bytes memory result = vm.ffi(cmds); + return abi.decode(result, (bytes32)); + } + + function hashOutputRootProof( + bytes32 _version, + bytes32 _stateRoot, + bytes32 _withdrawerStorageRoot, + bytes32 _latestBlockhash + ) external returns (bytes32) { + string[] memory cmds = new string[](7); + cmds[0] = "node"; + cmds[1] = "dist/scripts/differential-testing.js"; + cmds[2] = "hashOutputRootProof"; + cmds[3] = Strings.toHexString(uint256(_version)); + cmds[4] = Strings.toHexString(uint256(_stateRoot)); + cmds[5] = Strings.toHexString(uint256(_withdrawerStorageRoot)); + cmds[6] = Strings.toHexString(uint256(_latestBlockhash)); + + bytes memory result = vm.ffi(cmds); + return abi.decode(result, (bytes32)); + } + + function hashDepositTransaction( + address _from, + address _to, + uint256 _mint, + uint256 _value, + uint64 _gas, + bytes memory _data, + uint256 _logIndex + ) external returns (bytes32) { + string[] memory cmds = new string[](11); + cmds[0] = "node"; + cmds[1] = "dist/scripts/differential-testing.js"; + cmds[2] = "hashDepositTransaction"; + cmds[3] = "0x0000000000000000000000000000000000000000000000000000000000000000"; + cmds[4] = vm.toString(_logIndex); + cmds[5] = vm.toString(_from); + cmds[6] = vm.toString(_to); + cmds[7] = vm.toString(_mint); + cmds[8] = vm.toString(_value); + cmds[9] = vm.toString(_gas); + cmds[10] = vm.toString(_data); + bytes memory result = vm.ffi(cmds); + + return abi.decode(result, (bytes32)); + } + + function encodeCrossDomainMessage( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) external returns (bytes memory) { + string[] memory cmds = new string[](9); + cmds[0] = "node"; + cmds[1] = "dist/scripts/differential-testing.js"; + cmds[2] = "encodeCrossDomainMessage"; + cmds[3] = vm.toString(_nonce); + cmds[4] = vm.toString(_sender); + cmds[5] = vm.toString(_target); + cmds[6] = vm.toString(_value); + cmds[7] = vm.toString(_gasLimit); + cmds[8] = vm.toString(_data); + + bytes memory result = vm.ffi(cmds); + return abi.decode(result, (bytes)); + } + + function decodeVersionedNonce(uint256 nonce) external returns (uint256, uint256) { + string[] memory cmds = new string[](4); + cmds[0] = "node"; + cmds[1] = "dist/scripts/differential-testing.js"; + cmds[2] = "decodeVersionedNonce"; + cmds[3] = vm.toString(nonce); + + bytes memory result = vm.ffi(cmds); + return abi.decode(result, (uint256, uint256)); + } +} + // Used for testing a future upgrade beyond the current implementations. // We include some variables so that we can sanity check accessing storage values after an upgrade. contract NextImpl is Initializable { diff --git a/packages/contracts-bedrock/contracts/test/Encoding.t.sol b/packages/contracts-bedrock/contracts/test/Encoding.t.sol index 81ce802be5b6e..0e2ff776a6e2f 100644 --- a/packages/contracts-bedrock/contracts/test/Encoding.t.sol +++ b/packages/contracts-bedrock/contracts/test/Encoding.t.sol @@ -1,10 +1,14 @@ -//SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import { CommonTest } from "./CommonTest.t.sol"; import { Encoding } from "../libraries/Encoding.sol"; contract Encoding_Test is CommonTest { + function setUp() external { + _setUp(); + } + function test_nonceVersioning(uint240 _nonce, uint16 _version) external { (uint240 nonce, uint16 version) = Encoding.decodeVersionedNonce( Encoding.encodeVersionedNonce(_nonce, _version) @@ -12,4 +16,52 @@ contract Encoding_Test is CommonTest { assertEq(version, _version); assertEq(nonce, _nonce); } + + function test_decodeVersionedNonce_differential(uint240 _nonce, uint16 _version) external { + uint256 nonce = uint256(Encoding.encodeVersionedNonce(_nonce, _version)); + (uint256 decodedNonce, uint256 decodedVersion) = ffi.decodeVersionedNonce(nonce); + + assertEq( + _version, + uint16(decodedVersion) + ); + + assertEq( + _nonce, + uint240(decodedNonce) + ); + } + + function test_encodeCrossDomainMessage_differential( + uint240 _nonce, + uint8 _version, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) external { + uint8 version = _version % 2; + uint256 nonce = Encoding.encodeVersionedNonce(_nonce, version); + + bytes memory encoding = Encoding.encodeCrossDomainMessage( + nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + + bytes memory _encoding = ffi.encodeCrossDomainMessage( + nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + + assertEq(encoding, _encoding); + } } diff --git a/packages/contracts-bedrock/contracts/test/Hashing.t.sol b/packages/contracts-bedrock/contracts/test/Hashing.t.sol index d8650a78ec9ef..c0e949e225446 100644 --- a/packages/contracts-bedrock/contracts/test/Hashing.t.sol +++ b/packages/contracts-bedrock/contracts/test/Hashing.t.sol @@ -1,4 +1,4 @@ -//SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import { CommonTest } from "./CommonTest.t.sol"; @@ -6,6 +6,10 @@ import { Hashing } from "../libraries/Hashing.sol"; import { Encoding } from "../libraries/Encoding.sol"; contract Hashing_Test is CommonTest { + function setUp() external { + _setUp(); + } + function test_hashDepositSource() external { bytes32 sourceHash = Hashing.hashDepositSource( 0xd25df7858efc1778118fb133ac561b138845361626dfb976699c5287ed0f4959, @@ -17,4 +21,127 @@ contract Hashing_Test is CommonTest { 0xf923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc ); } + + function test_hashCrossDomainMessage_differential( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) external { + // Discard any fuzz tests with an invalid version + (, uint16 version) = Encoding.decodeVersionedNonce(_nonce); + vm.assume(version < 2); + + bytes32 _hash = ffi.hashCrossDomainMessage( + _nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + + bytes32 hash = Hashing.hashCrossDomainMessage( + _nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + + assertEq(hash, _hash); + } + + function test_hashWithdrawal_differential( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) external { + bytes32 hash = Hashing.hashWithdrawal( + _nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + + bytes32 _hash = ffi.hashWithdrawal( + _nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + + assertEq(hash, _hash); + } + + function test_hashOutputRootProof_differential( + bytes32 _version, + bytes32 _stateRoot, + bytes32 _withdrawerStorageRoot, + bytes32 _latestBlockhash + ) external { + Hashing.OutputRootProof memory proof = Hashing.OutputRootProof({ + version: _version, + stateRoot: _stateRoot, + withdrawerStorageRoot: _withdrawerStorageRoot, + latestBlockhash: _latestBlockhash + }); + + bytes32 hash = Hashing.hashOutputRootProof(proof); + + bytes32 _hash = ffi.hashOutputRootProof( + _version, + _stateRoot, + _withdrawerStorageRoot, + _latestBlockhash + ); + + assertEq(hash, _hash); + } + + // TODO(tynes): foundry bug cannot serialize + // bytes32 as strings with vm.toString + function test_hashDepositTransaction_differential( + address _from, + address _to, + uint256 _mint, + uint256 _value, + uint64 _gas, + bytes memory _data, + uint256 _logIndex + ) external { + bytes32 hash = Hashing.hashDepositTransaction( + _from, + _to, + _value, + _mint, + _gas, + false, // isCreate + _data, + bytes32(uint256(0)), + _logIndex + ); + + bytes32 _hash = ffi.hashDepositTransaction( + _from, + _to, + _mint, + _value, + _gas, + _data, + _logIndex + ); + + assertEq(hash, _hash); + } } diff --git a/packages/contracts-bedrock/contracts/test/OptimismPortal.t.sol b/packages/contracts-bedrock/contracts/test/OptimismPortal.t.sol index 8246042b1f86e..2251369940b56 100644 --- a/packages/contracts-bedrock/contracts/test/OptimismPortal.t.sol +++ b/packages/contracts-bedrock/contracts/test/OptimismPortal.t.sol @@ -1,8 +1,7 @@ -//SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import { Portal_Initializer, CommonTest, NextImpl } from "./CommonTest.t.sol"; - import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol"; @@ -30,8 +29,6 @@ contract OptimismPortal_Test is Portal_Initializer { assertEq(address(op).balance, 100); } - // function test_OptimismPortalDepositTransaction() external {} - // Test: depositTransaction fails when contract creation has a non-zero destination address function test_OptimismPortalContractCreationReverts() external { // contract creation must have a target of address(0) @@ -314,6 +311,84 @@ contract OptimismPortal_Test is Portal_Initializer { vm.expectRevert("L2OutputOracle: No output found for that block number."); assertEq(op.isBlockFinalized(checkpoint + 1), false); } + + function test_finalizeWithdrawalTransaction_differential( + address _sender, + address _target, + uint64 _value, + uint8 _gasLimit, + bytes memory _data + ) external { + // Cannot call the optimism portal + vm.assume(_target != address(op)); + + uint256 _nonce = messagePasser.nonce(); + + ( + bytes32 stateRoot, + bytes32 storageRoot, + bytes32 outputRoot, + bytes32 withdrawalHash, + bytes memory withdrawalProof + ) = ffi.getFinalizeWithdrawalTransactionInputs( + _nonce, + _sender, + _target, + _value, + uint256(_gasLimit), + _data + ); + + Hashing.OutputRootProof memory proof = Hashing.OutputRootProof({ + version: bytes32(uint256(0)), + stateRoot: stateRoot, + withdrawerStorageRoot: storageRoot, + latestBlockhash: bytes32(uint256(0)) + }); + + // Ensure the values returned from ffi are correct + assertEq(outputRoot, Hashing.hashOutputRootProof(proof)); + assertEq(withdrawalHash, Hashing.hashWithdrawal( + _nonce, + _sender, + _target, + _value, + uint64(_gasLimit), + _data + )); + + // Mock the call to the oracle + vm.mockCall( + address(oracle), + abi.encodeWithSelector(oracle.getL2Output.selector), + abi.encode(outputRoot, 0) + ); + + // Start the withdrawal, it must be initiated by the _sender and the + // correct value must be passed along + vm.deal(_sender, _value); + vm.prank(_sender); + messagePasser.initiateWithdrawal{ value: _value }( + _target, + uint256(_gasLimit), + _data + ); + // Ensure that the sentMessages is correct + assertEq(messagePasser.sentMessages(withdrawalHash), true); + + vm.warp(op.FINALIZATION_PERIOD_SECONDS() + 1); + op.finalizeWithdrawalTransaction{ value: _value }( + messagePasser.nonce() - 1, + _sender, + _target, + _value, + uint64(_gasLimit), + _data, + 100, // l2BlockNumber + proof, + withdrawalProof + ); + } } contract OptimismPortalUpgradeable_Test is Portal_Initializer { diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index d640cc7209147..a6ff109d2ba02 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -16,3 +16,8 @@ remappings = [ extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout'] bytecode_hash = 'none' build_info = true +ffi = true +fuzz_runs = 16 + +[ci] +fuzz_runs = 512 diff --git a/packages/contracts-bedrock/package.json b/packages/contracts-bedrock/package.json index 5a5dcdd17b108..9b6b18828ff22 100644 --- a/packages/contracts-bedrock/package.json +++ b/packages/contracts-bedrock/package.json @@ -19,7 +19,7 @@ "build": "hardhat compile && yarn build:ts && yarn typechain", "build:ts": "tsc -p tsconfig.json", "deploy": "hardhat deploy", - "test": "forge test", + "test": "yarn build:ts && forge test", "gas-snapshot": "forge snapshot", "storage-snapshot": "./scripts/storage-snapshot.sh", "slither": "./scripts/slither.sh", @@ -35,6 +35,8 @@ }, "dependencies": { "@eth-optimism/core-utils": "^0.9.1", + "@ethereumjs/trie": "^5.0.0-beta.1", + "@ethereumjs/util": "^8.0.0-beta.1", "@openzeppelin/contracts": "^4.5.0", "@openzeppelin/contracts-upgradeable": "^4.5.2", "@rari-capital/solmate": "https://github.com/rari-capital/solmate.git#8f9b23f8838670afda0fd8983f2c41e8037ae6bc", @@ -43,7 +45,7 @@ "ethereumjs-wallet": "^1.0.2", "ethers": "^5.6.8", "excessively-safe-call": "https://github.com/nomad-xyz/ExcessivelySafeCall.git#4fcdfd3593d21381f696c790fa6180b8ef559c1e", - "forge-std": "https://github.com/foundry-rs/forge-std.git#62caef29b0f87a2c6aaaf634b2ca4c09b6867c92", + "forge-std": "https://github.com/foundry-rs/forge-std.git#f18682b2874fc57d7c80a511fed0b35ec4201ffa", "hardhat": "^2.9.6", "merkle-patricia-tree": "^4.2.4", "rlp": "^2.2.7" diff --git a/packages/contracts-bedrock/scripts/differential-testing.ts b/packages/contracts-bedrock/scripts/differential-testing.ts new file mode 100644 index 0000000000000..5ed83b01a4fa4 --- /dev/null +++ b/packages/contracts-bedrock/scripts/differential-testing.ts @@ -0,0 +1,202 @@ +import { BigNumber, utils, constants } from 'ethers' +import { + decodeVersionedNonce, + hashCrossDomainMessage, + DepositTx, + SourceHashDomain, + encodeCrossDomainMessage, + hashWithdrawal, + hashOutputRootProof, +} from '@eth-optimism/core-utils' +import { SecureTrie } from '@ethereumjs/trie' +import { Account, Address, toBuffer, bufferToHex } from '@ethereumjs/util' + +import { predeploys } from '../src' + +const { hexZeroPad, RLP, keccak256 } = utils + +const args = process.argv.slice(2) +const command = args[0] + +;(async () => { + switch (command) { + case 'decodeVersionedNonce': { + const input = BigNumber.from(args[1]) + const [nonce, version] = decodeVersionedNonce(input) + + const output = utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [nonce.toHexString(), version.toHexString()] + ) + process.stdout.write(output) + break + } + case 'encodeCrossDomainMessage': { + const nonce = BigNumber.from(args[1]) + const sender = args[2] + const target = args[3] + const value = BigNumber.from(args[4]) + const gasLimit = BigNumber.from(args[5]) + const data = args[6] + + const encoding = encodeCrossDomainMessage( + nonce, + sender, + target, + value, + gasLimit, + data + ) + + const output = utils.defaultAbiCoder.encode(['bytes'], [encoding]) + process.stdout.write(output) + break + } + case 'hashCrossDomainMessage': { + const nonce = BigNumber.from(args[1]) + const sender = args[2] + const target = args[3] + const value = BigNumber.from(args[4]) + const gasLimit = BigNumber.from(args[5]) + const data = args[6] + + const hash = hashCrossDomainMessage( + nonce, + sender, + target, + value, + gasLimit, + data + ) + const output = utils.defaultAbiCoder.encode(['bytes32'], [hash]) + process.stdout.write(output) + break + } + case 'hashDepositTransaction': { + // The solidity transaction hash computation currently only works with + // user deposits. System deposit transaction hashing is not supported. + const l1BlockHash = args[1] + const logIndex = BigNumber.from(args[2]) + const from = args[3] + const to = args[4] + const mint = BigNumber.from(args[5]) + const value = BigNumber.from(args[6]) + const gas = BigNumber.from(args[7]) + const data = args[8] + + const tx = new DepositTx({ + l1BlockHash, + logIndex, + from, + to, + mint, + value, + gas, + data, + domain: SourceHashDomain.UserDeposit, + }) + + const digest = tx.hash() + const output = utils.defaultAbiCoder.encode(['bytes32'], [digest]) + process.stdout.write(output) + break + } + case 'hashWithdrawal': { + const nonce = BigNumber.from(args[1]) + const sender = args[2] + const target = args[3] + const value = BigNumber.from(args[4]) + const gas = BigNumber.from(args[5]) + const data = args[6] + + const hash = hashWithdrawal(nonce, sender, target, value, gas, data) + const output = utils.defaultAbiCoder.encode(['bytes32'], [hash]) + process.stdout.write(output) + break + } + case 'hashOutputRootProof': { + const version = hexZeroPad(BigNumber.from(args[1]).toHexString(), 32) + const stateRoot = hexZeroPad(BigNumber.from(args[2]).toHexString(), 32) + const withdrawerStorageRoot = hexZeroPad( + BigNumber.from(args[3]).toHexString(), + 32 + ) + const latestBlockhash = hexZeroPad( + BigNumber.from(args[4]).toHexString(), + 32 + ) + + const hash = hashOutputRootProof({ + version, + stateRoot, + withdrawerStorageRoot, + latestBlockhash, + }) + const output = utils.defaultAbiCoder.encode(['bytes32'], [hash]) + process.stdout.write(output) + break + } + case 'getFinalizeWithdrawalTransactionInputs': { + const nonce = BigNumber.from(args[1]) + const sender = args[2] + const target = args[3] + const value = BigNumber.from(args[4]) + const gas = BigNumber.from(args[5]) + const data = args[6] + + // Compute the withdrawalHash + const withdrawalHash = hashWithdrawal( + nonce, + sender, + target, + value, + gas, + data + ) + + // Compute the storage slot the withdrawalHash will be stored in + const slot = utils.defaultAbiCoder.encode( + ['bytes32', 'bytes32'], + [withdrawalHash, utils.hexZeroPad('0x', 32)] + ) + const key = keccak256(slot) + + // Create the account storage trie + const storage = new SecureTrie() + // Put a bool "true" into storage + await storage.put(toBuffer(key), toBuffer('0x01')) + + // Put the storage root into the L2ToL1MessagePasser storage + const address = Address.fromString(predeploys.L2ToL1MessagePasser) + const account = Account.fromAccountData({ + nonce: 0, + balance: 0, + stateRoot: storage.root, + }) + + const world = new SecureTrie() + await world.put(address.toBuffer(), account.serialize()) + + const proof = await SecureTrie.createProof(storage, toBuffer(key)) + + const outputRoot = hashOutputRootProof({ + version: constants.HashZero, + stateRoot: bufferToHex(world.root), + withdrawerStorageRoot: bufferToHex(storage.root), + latestBlockhash: constants.HashZero, + }) + + const encodedProof = RLP.encode(proof) + + const output = utils.defaultAbiCoder.encode( + ['bytes32', 'bytes32', 'bytes32', 'bytes32', 'bytes'], + [world.root, storage.root, outputRoot, withdrawalHash, encodedProof] + ) + process.stdout.write(output) + break + } + } +})().catch((err: Error) => { + console.error(err) + process.stdout.write('') +}) diff --git a/packages/contracts-bedrock/scripts/ffiMakeProof.ts b/packages/contracts-bedrock/scripts/ffiMakeProof.ts deleted file mode 100644 index 0180f82fe3232..0000000000000 --- a/packages/contracts-bedrock/scripts/ffiMakeProof.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Script for generating an inclusion proof for use in testing -// Intended for use with forge test --ffi, accepts abi encoded input and returns -// only the storageTrieWitness. -import { generateMockWithdrawalProof } from '../helpers' - -let args = process.argv.slice(2)[0] - -args = args - .replace('0x', '') - .split('') - .filter((char) => '0123456789abcdef'.includes(char)) - .join('') - -const main = async () => { - const proof = await generateMockWithdrawalProof('0x' + args) - console.log(proof.storageTrieWitness.slice(2)) -} -main() diff --git a/packages/contracts-bedrock/scripts/makeProof.ts b/packages/contracts-bedrock/scripts/makeProof.ts deleted file mode 100644 index 09989e90aaafe..0000000000000 --- a/packages/contracts-bedrock/scripts/makeProof.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Script for generating an inclusion proof for use in testing. -// Meant for manual usage, ie. -// ts-node scripts/makeProof.ts 1 0x0000000000000000000000000000000000000002 0x0000000000000000000000000000000000000003 4 500000 0x06 -import { generateMockWithdrawalProof } from '../helpers' - -const args = process.argv.slice(2) - -const [nonce, sender, target, value, gasLimit, data] = args - -const main = async () => { - const proof = await generateMockWithdrawalProof({ - nonce: +nonce, - sender, - target, - value: +value, - gasLimit: +gasLimit, - data, - }) - console.log(proof) -} -main() diff --git a/packages/contracts-bedrock/src/generateProofs.ts b/packages/contracts-bedrock/src/generateProofs.ts deleted file mode 100644 index 1315d7b55e2b9..0000000000000 --- a/packages/contracts-bedrock/src/generateProofs.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { ethers } from 'ethers' -import { toHexString } from '@eth-optimism/core-utils' - -import { TrieTestGenerator } from './trie-test-generator' -import { predeploys } from './constants' - -interface WithdrawalArgs { - nonce: number - sender: string - target: string - value: number - gasLimit: number - data: string -} - -interface OutputRootProof { - version: string - stateRoot: string - withdrawerStorageRoot: string - latestBlockhash: string -} - -export const deriveWithdrawalHash = (wd: WithdrawalArgs): string => { - return ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode( - ['uint256', 'address', 'address', 'uint256', 'uint256', 'bytes'], - [wd.nonce, wd.sender, wd.target, wd.value, wd.gasLimit, wd.data] - ) - ) -} - -export const generateMockWithdrawalProof = async ( - wd: WithdrawalArgs | string -): Promise<{ - outputRootProof: OutputRootProof - storageTrieWitness: string -}> => { - let withdrawalHash - if (typeof wd == 'string') { - // wd should be an abi encoded string - withdrawalHash = ethers.utils.keccak256(wd) - } else { - withdrawalHash = deriveWithdrawalHash(wd as WithdrawalArgs) - } - - const storageKey = ethers.utils.keccak256( - ethers.utils.hexConcat([ - withdrawalHash, - ethers.utils.hexZeroPad('0x01', 32), - ]) - ) - - const storageGenerator = await TrieTestGenerator.fromNodes({ - nodes: [ - { - key: storageKey, - val: '0x' + '01'.padStart(2, '0'), - }, - ], - secure: true, - }) - - const generator = await TrieTestGenerator.fromAccounts({ - accounts: [ - { - address: predeploys.L2ToL1MessagePasser, - nonce: 0, - balance: 0, - codeHash: ethers.utils.keccak256('0x1234'), - storageRoot: toHexString(storageGenerator._trie.root), - }, - ], - secure: true, - }) - - return { - outputRootProof: { - version: ethers.constants.HashZero, - stateRoot: toHexString(generator._trie.root), - withdrawerStorageRoot: toHexString(storageGenerator._trie.root), - latestBlockhash: ethers.constants.HashZero, - }, - storageTrieWitness: ( - await storageGenerator.makeInclusionProofTest(storageKey) - ).proof, - } -} - -export const generateOutputRoot = (outputElements: { - version: string - stateRoot: string - withdrawerStorageRoot: string - latestBlockhash: string -}) => { - const { version, stateRoot, withdrawerStorageRoot, latestBlockhash } = - outputElements - return ethers.utils.solidityKeccak256( - ['bytes32', 'bytes32', 'bytes32', 'bytes32'], - [version, stateRoot, withdrawerStorageRoot, latestBlockhash] - ) -} diff --git a/packages/contracts-bedrock/src/index.ts b/packages/contracts-bedrock/src/index.ts index e481028f072b3..f87cf0102a145 100644 --- a/packages/contracts-bedrock/src/index.ts +++ b/packages/contracts-bedrock/src/index.ts @@ -1,2 +1 @@ -export * from './generateProofs' export * from './constants' diff --git a/packages/contracts-bedrock/src/trie-test-generator.ts b/packages/contracts-bedrock/src/trie-test-generator.ts deleted file mode 100644 index 8e5bd6caed1b1..0000000000000 --- a/packages/contracts-bedrock/src/trie-test-generator.ts +++ /dev/null @@ -1,261 +0,0 @@ -/* External Imports */ -import * as rlp from 'rlp' -// import { default as seedbytes } from 'random-bytes-seed' -import { SecureTrie, BaseTrie } from 'merkle-patricia-tree' -import { fromHexString, toHexString } from '@eth-optimism/core-utils' -import { ethers } from 'ethers' - -interface TrieNode { - key: string - val: string -} - -interface InclusionProofTest { - key: string - val: string - proof: string - root: string -} - -interface NodeUpdateTest extends InclusionProofTest { - newRoot: string -} - -interface EthereumAccount { - address?: string - nonce: number - balance: number - codeHash: string - storageRoot?: string - storage?: TrieNode[] -} - -interface AccountProofTest { - address: string - account: EthereumAccount - accountTrieWitness: string - accountTrieRoot: string -} - -interface AccountUpdateTest extends AccountProofTest { - newAccountTrieRoot: string -} - -const rlpEncodeAccount = (account: EthereumAccount): string => { - return toHexString( - rlp.encode([ - account.nonce, - account.balance, - account.storageRoot || ethers.constants.HashZero, - account.codeHash || ethers.constants.HashZero, - ]) - ) -} - -const rlpDecodeAccount = (encoded: string): EthereumAccount => { - const decoded = rlp.decode(fromHexString(encoded)) as any - return { - nonce: decoded[0].length ? parseInt(decoded[0], 16) : 0, - balance: decoded[1].length ? parseInt(decoded[1], 16) : 0, - storageRoot: decoded[2].length - ? toHexString(decoded[2]) - : ethers.constants.HashZero, - codeHash: decoded[3].length - ? toHexString(decoded[3]) - : ethers.constants.HashZero, - } -} - -const makeTrie = async ( - nodes: TrieNode[], - secure?: boolean -): Promise<{ - trie: SecureTrie | BaseTrie - TrieClass: any -}> => { - const TrieClass = secure ? SecureTrie : BaseTrie - const trie = new TrieClass() - - for (const node of nodes) { - await trie.put(fromHexString(node.key), fromHexString(node.val)) - } - - return { - trie, - TrieClass, - } -} - -export class TrieTestGenerator { - constructor( - public _TrieClass: any, - public _trie: SecureTrie | BaseTrie, - public _nodes: TrieNode[], - public _subGenerators?: TrieTestGenerator[] - ) {} - - static async fromNodes(opts: { - nodes: TrieNode[] - secure?: boolean - }): Promise { - const { trie, TrieClass } = await makeTrie(opts.nodes, opts.secure) - - return new TrieTestGenerator(TrieClass, trie, opts.nodes) - } - - // static async fromRandom(opts: { - // seed: string - // nodeCount: number - // secure?: boolean - // keySize?: number - // valSize?: number - // }): Promise { - // const getRandomBytes = seedbytes(opts.seed) - // const nodes: TrieNode[] = [...Array(opts.nodeCount)].map(() => { - // return { - // key: toHexString(getRandomBytes(opts.keySize || 32)), - // val: toHexString(getRandomBytes(opts.valSize || 32)), - // } - // }) - - // return TrieTestGenerator.fromNodes({ - // nodes, - // secure: opts.secure, - // }) - // } - - static async fromAccounts(opts: { - accounts: EthereumAccount[] - secure?: boolean - }): Promise { - const subGenerators: TrieTestGenerator[] = [] - - for (const account of opts.accounts) { - if (account.storage) { - const subGenerator = await TrieTestGenerator.fromNodes({ - nodes: account.storage, - secure: opts.secure, - }) - - account.storageRoot = toHexString(subGenerator._trie.root) - subGenerators.push(subGenerator) - } - } - - const nodes = opts.accounts.map((account) => { - return { - key: account.address as string, - val: rlpEncodeAccount(account), - } - }) - - const { trie, TrieClass } = await makeTrie(nodes, opts.secure) - - return new TrieTestGenerator(TrieClass, trie, nodes, subGenerators) - } - - public async makeInclusionProofTest( - key: string | number - ): Promise { - if (typeof key === 'number') { - key = this._nodes[key].key - } - - const trie = this._trie.copy() - - const proof = await this.prove(key) - const val = await trie.get(fromHexString(key)) - - return { - proof: toHexString(rlp.encode(proof)), - key: toHexString(key), - val: toHexString(val), - root: toHexString(trie.root), - } - } - - public async makeAllInclusionProofTests(): Promise { - return Promise.all( - this._nodes.map(async (node) => { - return this.makeInclusionProofTest(node.key) - }) - ) - } - - public async makeNodeUpdateTest( - key: string | number, - val: string - ): Promise { - if (typeof key === 'number') { - key = this._nodes[key].key - } - - const trie = this._trie.copy() - - const proof = await this.prove(key) - const oldRoot = trie.root - - await trie.put(fromHexString(key), fromHexString(val)) - const newRoot = trie.root - - return { - proof: toHexString(rlp.encode(proof)), - key: toHexString(key), - val: toHexString(val), - root: toHexString(oldRoot), - newRoot: toHexString(newRoot), - } - } - - public async makeAccountProofTest( - address: string | number - ): Promise { - if (typeof address === 'number') { - address = this._nodes[address].key - } - - const trie = this._trie.copy() - - const proof = await this.prove(address) - const account = await trie.get(fromHexString(address)) - - return { - address, - account: rlpDecodeAccount(toHexString(account)), - accountTrieWitness: toHexString(rlp.encode(proof)), - accountTrieRoot: toHexString(trie.root), - } - } - - public async makeAccountUpdateTest( - address: string | number, - account: EthereumAccount - ): Promise { - if (typeof address === 'number') { - address = this._nodes[address].key - } - - const trie = this._trie.copy() - - const proof = await this.prove(address) - const oldRoot = trie.root - - await trie.put( - fromHexString(address), - fromHexString(rlpEncodeAccount(account)) - ) - const newRoot = trie.root - - return { - address, - account, - accountTrieWitness: toHexString(rlp.encode(proof)), - accountTrieRoot: toHexString(oldRoot), - newAccountTrieRoot: toHexString(newRoot), - } - } - - private async prove(key: string): Promise { - return this._TrieClass.prove(this._trie, fromHexString(key)) - } -} diff --git a/packages/contracts-bedrock/test/helpers.spec.ts b/packages/contracts-bedrock/test/helpers.spec.ts deleted file mode 100644 index e476f309db89c..0000000000000 --- a/packages/contracts-bedrock/test/helpers.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { expect } from 'chai' -import { BigNumber } from 'ethers' - -import { DepositTx, SourceHashDomain } from '../src' - -describe('Helpers', () => { - describe('DepositTx', () => { - // TODO(tynes): this is out of date now that the subversion - // byte has been added - it('should serialize/deserialize and hash', () => { - // constants serialized using optimistic-geth - // TODO(tynes): more tests - const hash = - '0xf58e30138cb01330f6450b9a5e717a63840ad2e21f17340105b388ad3c668749' - const raw = - '0x7e00f862a0f923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc94f39fd6e51aad88f6f4ce6ab8827279cfffb9226694b79f76ef2c5f0286176833e7b2eee103b1cc3244880e043da617250000880de0b6b3a7640000832dc6c080' - - const tx = new DepositTx({ - from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - gas: '0x2dc6c0', - data: '0x', - to: '0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244', - value: '0xde0b6b3a7640000', - domain: SourceHashDomain.UserDeposit, - l1BlockHash: - '0xd25df7858efc1778118fb133ac561b138845361626dfb976699c5287ed0f4959', - logIndex: 1, - mint: '0xe043da617250000', - }) - - const sourceHash = tx.sourceHash() - expect(sourceHash).to.deep.eq( - '0xf923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc' - ) - - const encoded = tx.encode() - expect(encoded).to.deep.eq(raw) - const hashed = tx.hash() - expect(hashed).to.deep.eq(hash) - - const decoded = DepositTx.decode(raw, { - domain: SourceHashDomain.UserDeposit, - l1BlockHash: tx.l1BlockHash, - logIndex: tx.logIndex, - }) - expect(decoded.from).to.deep.eq(tx.from) - expect(decoded.gas).to.deep.eq(BigNumber.from(tx.gas)) - expect(decoded.data).to.deep.eq(tx.data) - expect(decoded.to).to.deep.eq(tx.to) - expect(decoded.value).to.deep.eq(BigNumber.from(tx.value)) - expect(decoded.domain).to.deep.eq(SourceHashDomain.UserDeposit) - expect(decoded.l1BlockHash).to.deep.eq(tx.l1BlockHash) - expect(decoded.logIndex).to.deep.eq(tx.logIndex) - expect(decoded.mint).to.deep.eq(BigNumber.from(tx.mint)) - }) - }) -}) diff --git a/packages/contracts-bedrock/tsconfig.json b/packages/contracts-bedrock/tsconfig.json index cfdbeb7b68f4e..881cf3f5d38bc 100644 --- a/packages/contracts-bedrock/tsconfig.json +++ b/packages/contracts-bedrock/tsconfig.json @@ -1,9 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "rootDir": "./src", + "rootDir": ".", "outDir": "./dist" }, "exclude": ["hardhat.config.ts", "deploy", "tasks", "test"], - "include": ["src/**/*"] + "include": ["src/**/*", "scripts/differential-testing.ts"] } diff --git a/packages/contracts-periphery/package.json b/packages/contracts-periphery/package.json index 0c7e963a42536..d879008515cea 100644 --- a/packages/contracts-periphery/package.json +++ b/packages/contracts-periphery/package.json @@ -77,7 +77,7 @@ "ds-test": "https://github.com/dapphub/ds-test.git#9310e879db8ba3ea6d5c6489a579118fd264a3f5", "ethereum-waffle": "^3.4.4", "ethers": "^5.6.8", - "forge-std": "https://github.com/foundry-rs/forge-std.git#62caef29b0f87a2c6aaaf634b2ca4c09b6867c92", + "forge-std": "https://github.com/foundry-rs/forge-std.git#f18682b2874fc57d7c80a511fed0b35ec4201ffa", "hardhat": "^2.9.6", "hardhat-deploy": "^0.11.10", "hardhat-gas-reporter": "^1.0.8", diff --git a/packages/core-utils/src/optimism/encoding.ts b/packages/core-utils/src/optimism/encoding.ts new file mode 100644 index 0000000000000..cb1f58fa73c0b --- /dev/null +++ b/packages/core-utils/src/optimism/encoding.ts @@ -0,0 +1,120 @@ +import { ethers, BigNumberish, BigNumber } from 'ethers' + +const iface = new ethers.utils.Interface([ + 'function relayMessage(address,address,bytes,uint256)', + 'function relayMessage(uint256,address,address,uint256,uint256,bytes)', +]) + +const nonceMask = BigNumber.from( + '0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +) + +export const big0 = BigNumber.from(0) +export const big1 = BigNumber.from(1) + +/** + * Encodes the version into the nonce. + * + * @param nonce + * @param version + */ +export const encodeVersionedNonce = ( + nonce: BigNumber, + version: BigNumber +): BigNumber => { + return version.or(nonce.shl(240)) +} + +/** + * Decodes the version from the nonce and returns the unversioned nonce as well + * as the version. The version is encoded in the first byte of + * the nonce. Note that this nonce is the nonce held in the + * CrossDomainMessenger. + * + * @param nonce + */ +export const decodeVersionedNonce = (nonce: BigNumber): BigNumber[] => { + return [nonce.and(nonceMask), nonce.shr(240)] +} + +/** + * Encodes a V1 cross domain message. This message format was used before + * bedrock and does not support value transfer because ETH was represented as an + * ERC20 natively. + * + * @param target The target of the cross domain message + * @param sender The sender of the cross domain message + * @param data The data passed along with the cross domain message + * @param nonce The cross domain message nonce + */ +export const encodeCrossDomainMessageV0 = ( + target: string, + sender: string, + data: string, + nonce: BigNumber +) => { + return iface.encodeFunctionData( + 'relayMessage(address,address,bytes,uint256)', + [target, sender, data, nonce] + ) +} + +/** + * Encodes a V1 cross domain message. This message format shipped with bedrock + * and supports value transfer with native ETH. + * + * @param nonce The cross domain message nonce + * @param sender The sender of the cross domain message + * @param target The target of the cross domain message + * @param value The value being sent with the cross domain message + * @param gasLimit The gas limit of the cross domain execution + * @param data The data passed along with the cross domain message + */ +export const encodeCrossDomainMessageV1 = ( + nonce: BigNumber, + sender: string, + target: string, + value: BigNumberish, + gasLimit: BigNumberish, + data: string +) => { + return iface.encodeFunctionData( + 'relayMessage(uint256,address,address,uint256,uint256,bytes)', + [nonce, sender, target, value, gasLimit, data] + ) +} + +/** + * Encodes a cross domain message. The version byte in the nonce determines + * the serialization format that is used. + * + * @param nonce The cross domain message nonce + * @param sender The sender of the cross domain message + * @param target The target of the cross domain message + * @param value The value being sent with the cross domain message + * @param gasLimit The gas limit of the cross domain execution + * @param data The data passed along with the cross domain message + */ +export const encodeCrossDomainMessage = ( + nonce: BigNumber, + sender: string, + target: string, + value: BigNumber, + gasLimit: BigNumber, + data: string +) => { + const [, version] = decodeVersionedNonce(nonce) + if (version.eq(big0)) { + return encodeCrossDomainMessageV0(target, sender, data, nonce) + } else if (version.eq(big1)) { + return encodeCrossDomainMessageV1( + nonce, + sender, + target, + value, + gasLimit, + data + ) + } + throw new Error(`unknown version ${version.toString()}`) +} diff --git a/packages/core-utils/src/optimism/hashing.ts b/packages/core-utils/src/optimism/hashing.ts new file mode 100644 index 0000000000000..d639d18366a53 --- /dev/null +++ b/packages/core-utils/src/optimism/hashing.ts @@ -0,0 +1,140 @@ +import { BigNumberish, BigNumber, utils } from 'ethers' +const { keccak256, defaultAbiCoder } = utils + +import { + decodeVersionedNonce, + encodeCrossDomainMessageV0, + encodeCrossDomainMessageV1, + big0, + big1, +} from './encoding' + +export interface OutputRootProof { + version: string + stateRoot: string + withdrawerStorageRoot: string + latestBlockhash: string +} + +/** + * Hahses a cross domain message. + * + * @param nonce The cross domain message nonce + * @param sender The sender of the cross domain message + * @param target The target of the cross domain message + * @param value The value being sent with the cross domain message + * @param gasLimit The gas limit of the cross domain execution + * @param data The data passed along with the cross domain message + */ +export const hashCrossDomainMessage = ( + nonce: BigNumber, + sender: string, + target: string, + value: BigNumber, + gasLimit: BigNumber, + data: string +) => { + const [, version] = decodeVersionedNonce(nonce) + if (version.eq(big0)) { + return hashCrossDomainMessagev0(target, sender, data, nonce) + } else if (version.eq(big1)) { + return hashCrossDomainMessagev1( + nonce, + sender, + target, + value, + gasLimit, + data + ) + } + throw new Error(`unknown version ${version.toString()}`) +} + +/** + * Hahses a V0 cross domain message + * + * @param target The target of the cross domain message + * @param sender The sender of the cross domain message + * @param data The data passed along with the cross domain message + * @param nonce The cross domain message nonce + */ +export const hashCrossDomainMessagev0 = ( + target: string, + sender: string, + data: string, + nonce: BigNumber +) => { + return keccak256(encodeCrossDomainMessageV0(target, sender, data, nonce)) +} + +/** + * Hahses a V1 cross domain message + * + * @param nonce The cross domain message nonce + * @param sender The sender of the cross domain message + * @param target The target of the cross domain message + * @param value The value being sent with the cross domain message + * @param gasLimit The gas limit of the cross domain execution + * @param data The data passed along with the cross domain message + */ +export const hashCrossDomainMessagev1 = ( + nonce: BigNumber, + sender: string, + target: string, + value: BigNumberish, + gasLimit: BigNumberish, + data: string +) => { + return keccak256( + encodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, data) + ) +} + +/** + * Hashes a withdrawal + * + * @param nonce The cross domain message nonce + * @param sender The sender of the cross domain message + * @param target The target of the cross domain message + * @param value The value being sent with the cross domain message + * @param gasLimit The gas limit of the cross domain execution + * @param data The data passed along with the cross domain message + */ +export const hashWithdrawal = ( + nonce: BigNumber, + sender: string, + target: string, + value: BigNumber, + gasLimit: BigNumber, + data: string +): string => { + const types = ['uint256', 'address', 'address', 'uint256', 'uint256', 'bytes'] + const encoded = defaultAbiCoder.encode(types, [ + nonce, + sender, + target, + value, + gasLimit, + data, + ]) + return keccak256(encoded) +} + +/** + * Hahses an output root proof + * + * @param proof OutputRootProof + */ +export const hashOutputRootProof = (proof: OutputRootProof): string => { + return keccak256( + defaultAbiCoder.encode( + ['bytes32', 'bytes32', 'bytes32', 'bytes32'], + [ + proof.version, + proof.stateRoot, + proof.withdrawerStorageRoot, + proof.latestBlockhash, + ] + ) + ) +} diff --git a/packages/core-utils/src/optimism/index.ts b/packages/core-utils/src/optimism/index.ts index 36b50eb509fca..5691eb6528542 100644 --- a/packages/core-utils/src/optimism/index.ts +++ b/packages/core-utils/src/optimism/index.ts @@ -8,3 +8,5 @@ export * from './fees' export * from './rollup-types' export * from './op-node' export * from './deposit-transaction' +export * from './encoding' +export * from './hashing' diff --git a/yarn.lock b/yarn.lock index 3e4f5d33b6568..a9dda0327d9c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -827,6 +827,20 @@ ethereumjs-util "^7.1.1" miller-rabin "^4.0.0" +"@ethereumjs/trie@^5.0.0-beta.1": + version "5.0.0-beta.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/trie/-/trie-5.0.0-beta.1.tgz#79d1108222b45bc3576d62583364c96626ce4175" + integrity sha512-OjTzt9fK5aMzm84GRSe+C7bO2zorbEWRueLbxOMlS7lHCiXA7akIQ3mzz9VBSMjT7m01hZ1r3fZIOGHzQVCHtw== + dependencies: + "@ethereumjs/util" "8.0.0-beta.1" + abstract-level "^1.0.3" + ethereum-cryptography "^1.0.3" + level "^8.0.0" + memory-level "^1.0.0" + readable-stream "^3.6.0" + rlp "4.0.0-beta.1" + semaphore-async-await "^1.5.1" + "@ethereumjs/tx@^3.2.1": version "3.3.0" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.0.tgz#14ed1b7fa0f28e1cd61e3ecbdab824205f6a4378" @@ -851,6 +865,14 @@ "@ethereumjs/common" "^2.6.3" ethereumjs-util "^7.1.4" +"@ethereumjs/util@8.0.0-beta.1", "@ethereumjs/util@^8.0.0-beta.1": + version "8.0.0-beta.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.0-beta.1.tgz#369526faf6e9f1cadfd39c7741cc07cf33d128f8" + integrity sha512-yUg3TdJm25HiamAXbNuOagXQPmgdSrV3oEH0h+Adsxt6D7qHw8HyHLA8C+tNrLP2YwcjF1dGJ+F7WtOibzEp9g== + dependencies: + ethereum-cryptography "^1.0.3" + rlp "4.0.0-beta.1" + "@ethereumjs/vm@^5.9.0": version "5.9.0" resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.9.0.tgz#54e485097c6dbb42554d541ef8d84d06b7ddf12f" @@ -3183,14 +3205,6 @@ fs-extra "^9.1.0" lodash "^4.17.15" -"@typechain/hardhat@^6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-6.1.2.tgz#d3beccc6937d93f9b437616b741f839a8b953693" - integrity sha512-k4Ea3pVITKB2DH8p1a5U38cyy7KZPD04Spo4q5b4wO+n2mT+uAz5dxckPtbczn/Kk5wiFq+ZkuOtw5ZKFhL/+w== - dependencies: - fs-extra "^9.1.0" - lodash "^4.17.15" - "@types/abstract-leveldown@*": version "5.0.2" resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.2.tgz#ee81917fe38f770e29eec8139b6f16ee4a8b0a5f" @@ -3896,6 +3910,19 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" + integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== + dependencies: + buffer "^6.0.3" + catering "^2.1.0" + is-buffer "^2.0.5" + level-supports "^4.0.0" + level-transcoder "^1.0.1" + module-error "^1.0.1" + queue-microtask "^1.2.3" + abstract-leveldown@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz#5cb89f958a44f526779d740d1440e743e0c30a57" @@ -5310,6 +5337,16 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= +browser-level@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011" + integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.1" + module-error "^1.0.2" + run-parallel-limit "^1.1.0" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -5444,6 +5481,14 @@ buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + bufferutil@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" @@ -5669,6 +5714,11 @@ caseless@^0.12.0, caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +catering@^2.1.0, catering@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" + integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== + cbor@^5.0.2: version "5.2.0" resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" @@ -5901,6 +5951,17 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classic-level@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.2.0.tgz#2d52bdec8e7a27f534e67fdeb890abef3e643c27" + integrity sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.0" + module-error "^1.0.1" + napi-macros "~2.0.0" + node-gyp-build "^4.3.0" + clean-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" @@ -8994,9 +9055,9 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -"forge-std@https://github.com/foundry-rs/forge-std.git#62caef29b0f87a2c6aaaf634b2ca4c09b6867c92": +"forge-std@https://github.com/foundry-rs/forge-std.git#f18682b2874fc57d7c80a511fed0b35ec4201ffa": version "0.0.0" - resolved "https://github.com/foundry-rs/forge-std.git#62caef29b0f87a2c6aaaf634b2ca4c09b6867c92" + resolved "https://github.com/foundry-rs/forge-std.git#f18682b2874fc57d7c80a511fed0b35ec4201ffa" form-data@^2.2.0: version "2.5.1" @@ -10113,7 +10174,7 @@ idna-uts46-hx@^2.3.1: dependencies: punycode "2.1.0" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -10399,7 +10460,7 @@ is-buffer@^1.1.4, is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@~2.0.3: +is-buffer@^2.0.5, is-buffer@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== @@ -11399,6 +11460,11 @@ level-sublevel@6.6.4: typewiselite "~1.0.0" xtend "~4.0.0" +level-supports@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" + integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== + level-supports@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" @@ -11406,6 +11472,14 @@ level-supports@~1.0.0: dependencies: xtend "^4.0.2" +level-transcoder@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" + integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== + dependencies: + buffer "^6.0.3" + module-error "^1.0.1" + level-ws@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" @@ -11441,6 +11515,14 @@ level@^6.0.1: level-packager "^5.1.0" leveldown "^5.4.0" +level@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/level/-/level-8.0.0.tgz#41b4c515dabe28212a3e881b61c161ffead14394" + integrity sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ== + dependencies: + browser-level "^1.0.1" + classic-level "^1.2.0" + leveldown@^5.4.0: version "5.6.0" resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" @@ -12102,6 +12184,15 @@ memdown@~3.0.0: ltgt "~2.2.0" safe-buffer "~5.1.1" +memory-level@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" + integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og== + dependencies: + abstract-level "^1.0.0" + functional-red-black-tree "^1.0.1" + module-error "^1.0.1" + memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" @@ -12665,6 +12756,11 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +module-error@^1.0.1, module-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" + integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== + morgan@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" @@ -12910,6 +13006,11 @@ node-gyp-build@^4.2.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== +node-gyp-build@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" + integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + node-gyp-build@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" @@ -14428,7 +14529,7 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -queue-microtask@^1.2.2: +queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== @@ -15139,6 +15240,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rlp@4.0.0-beta.1: + version "4.0.0-beta.1" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-4.0.0-beta.1.tgz#46983ee758344e5eee48f135129407434cfea2b6" + integrity sha512-UVIENF7Rw+nX5cpfzw6X3/oXNQKsSZ8HbDJUeU9RoIs1LLyMjcPZR1o26i1vFbpuVN8GRmcdopEYOMjVsLRsQQ== + rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" @@ -15163,6 +15269,13 @@ run-async@^2.2.0, run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-parallel-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" + integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== + dependencies: + queue-microtask "^1.2.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"