Commit c75a0fc8 authored by smartcontracts's avatar smartcontracts Committed by GitHub

Port SequencerEntrypoint to use ovm solc (#535)

* port SequencerEntrypoint to use ovm solc

* Minor tweaks and comments

* have hardhat-ovm invalidate cache on commit to solc-bin

* remove temporary solc version override

* have cache invalidation on a per-version basis

* remove ethereumjs-util dep

* fix lint error

* use allow_kall_2 compiler until build is fixed

* add changeset
parent c7bc0cef
---
"@eth-optimism/contracts": patch
"@eth-optimism/hardhat-ovm": patch
---
Use optimistic-solc to compile the SequencerEntrypoint. Also introduces a cache invalidation mechanism for hardhat-ovm so that we can push new compiler versions.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// @unsupported: evm
pragma solidity >0.5.0 <0.8.0; pragma solidity >0.5.0 <0.8.0;
/* Interface Imports */
import { iOVM_ECDSAContractAccount } from "../../iOVM/accounts/iOVM_ECDSAContractAccount.sol";
/* Library Imports */ /* Library Imports */
import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol"; import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol"; import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; import { Lib_ExecutionManagerWrapper } from "../../libraries/wrappers/Lib_ExecutionManagerWrapper.sol";
/** /**
* @title OVM_SequencerEntrypoint * @title OVM_SequencerEntrypoint
...@@ -15,7 +19,7 @@ import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_Sa ...@@ -15,7 +19,7 @@ import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_Sa
* This contract is the implementation referenced by the Proxy Sequencer Entrypoint, thus enabling * This contract is the implementation referenced by the Proxy Sequencer Entrypoint, thus enabling
* the Optimism team to upgrade the decompression of calldata from the Sequencer. * the Optimism team to upgrade the decompression of calldata from the Sequencer.
* *
* Compiler used: solc * Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_SequencerEntrypoint { contract OVM_SequencerEntrypoint {
...@@ -59,41 +63,54 @@ contract OVM_SequencerEntrypoint { ...@@ -59,41 +63,54 @@ contract OVM_SequencerEntrypoint {
bytes memory compressedTx = Lib_BytesUtils.slice(msg.data, 66); bytes memory compressedTx = Lib_BytesUtils.slice(msg.data, 66);
bool isEthSignedMessage = transactionType == TransactionType.ETH_SIGNED_MESSAGE; bool isEthSignedMessage = transactionType == TransactionType.ETH_SIGNED_MESSAGE;
// Grab the chain ID for the current network.
uint256 chainId;
assembly {
chainId := chainid()
}
// Need to decompress and then re-encode the transaction based on the original encoding. // Need to decompress and then re-encode the transaction based on the original encoding.
bytes memory encodedTx = Lib_OVMCodec.encodeEIP155Transaction( bytes memory encodedTx = Lib_OVMCodec.encodeEIP155Transaction(
Lib_OVMCodec.decompressEIP155Transaction(compressedTx), Lib_OVMCodec.decompressEIP155Transaction(
compressedTx,
chainId
),
isEthSignedMessage isEthSignedMessage
); );
address target = Lib_ECDSAUtils.recover( address target = Lib_ECDSAUtils.recover(
encodedTx, encodedTx,
isEthSignedMessage, isEthSignedMessage,
uint8(v), v,
r, r,
s s
); );
if (Lib_SafeExecutionManagerWrapper.safeEXTCODESIZE(target) == 0) { bool isEmptyContract;
assembly {
isEmptyContract := iszero(extcodesize(target))
}
if (isEmptyContract) {
// ProxyEOA has not yet been deployed for this EOA. // ProxyEOA has not yet been deployed for this EOA.
bytes32 messageHash = Lib_ECDSAUtils.getMessageHash(encodedTx, isEthSignedMessage); bytes32 messageHash = Lib_ECDSAUtils.getMessageHash(encodedTx, isEthSignedMessage);
Lib_SafeExecutionManagerWrapper.safeCREATEEOA(messageHash, uint8(v), r, s); Lib_ExecutionManagerWrapper.ovmCREATEEOA(messageHash, v, r, s);
}
Lib_OVMCodec.EOASignatureType sigtype;
if (isEthSignedMessage) {
sigtype = Lib_OVMCodec.EOASignatureType.ETH_SIGNED_MESSAGE;
} else {
sigtype = Lib_OVMCodec.EOASignatureType.EIP155_TRANSACTION;
} }
// ProxyEOA has been deployed for this EOA, continue to CALL. iOVM_ECDSAContractAccount(target).execute(
bytes memory callbytes = abi.encodeWithSignature(
"execute(bytes,uint8,uint8,bytes32,bytes32)",
encodedTx, encodedTx,
isEthSignedMessage, sigtype,
uint8(v), v,
r, r,
s s
); );
Lib_SafeExecutionManagerWrapper.safeCALL(
gasleft(),
target,
callbytes
);
} }
...@@ -119,9 +136,7 @@ contract OVM_SequencerEntrypoint { ...@@ -119,9 +136,7 @@ contract OVM_SequencerEntrypoint {
} if (_transactionType == 2) { } if (_transactionType == 2) {
return TransactionType.ETH_SIGNED_MESSAGE; return TransactionType.ETH_SIGNED_MESSAGE;
} else { } else {
Lib_SafeExecutionManagerWrapper.safeREVERT( revert("Transaction type must be 0 or 2");
"Transaction type must be 0 or 2"
);
} }
} }
} }
...@@ -155,10 +155,12 @@ library Lib_OVMCodec { ...@@ -155,10 +155,12 @@ library Lib_OVMCodec {
/** /**
* Decompresses a compressed EIP155 transaction. * Decompresses a compressed EIP155 transaction.
* @param _transaction Compressed EIP155 transaction bytes. * @param _transaction Compressed EIP155 transaction bytes.
* @param _chainId Chain ID this transaction was signed with.
* @return Transaction parsed into a struct. * @return Transaction parsed into a struct.
*/ */
function decompressEIP155Transaction( function decompressEIP155Transaction(
bytes memory _transaction bytes memory _transaction,
uint256 _chainId
) )
internal internal
returns ( returns (
...@@ -171,7 +173,7 @@ library Lib_OVMCodec { ...@@ -171,7 +173,7 @@ library Lib_OVMCodec {
nonce: Lib_BytesUtils.toUint24(_transaction, 6), nonce: Lib_BytesUtils.toUint24(_transaction, 6),
to: Lib_BytesUtils.toAddress(_transaction, 9), to: Lib_BytesUtils.toAddress(_transaction, 9),
data: Lib_BytesUtils.slice(_transaction, 29), data: Lib_BytesUtils.slice(_transaction, 29),
chainId: Lib_SafeExecutionManagerWrapper.safeCHAINID(), chainId: _chainId,
value: 0 value: 0
}); });
} }
......
// SPDX-License-Identifier: MIT
// @unsupported: evm
pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_ErrorUtils } from "../utils/Lib_ErrorUtils.sol";
/**
* @title Lib_ExecutionManagerWrapper
*
* Compiler used: solc
* Runtime target: OVM
*/
library Lib_ExecutionManagerWrapper {
/**********************
* Internal Functions *
**********************/
/**
* Performs a safe ovmGETNONCE call.
* @return _nonce Result of calling ovmGETNONCE.
*/
function ovmGETNONCE()
internal
returns (
uint256 _nonce
)
{
bytes memory returndata = _safeExecutionManagerInteraction(
abi.encodeWithSignature(
"ovmGETNONCE()"
)
);
return abi.decode(returndata, (uint256));
}
/**
* Performs a safe ovmINCREMENTNONCE call.
*/
function ovmINCREMENTNONCE()
internal
{
_safeExecutionManagerInteraction(
abi.encodeWithSignature(
"ovmINCREMENTNONCE()"
)
);
}
/**
* Performs a safe ovmCREATEEOA call.
* @param _messageHash Message hash which was signed by EOA
* @param _v v value of signature (0 or 1)
* @param _r r value of signature
* @param _s s value of signature
*/
function ovmCREATEEOA(
bytes32 _messageHash,
uint8 _v,
bytes32 _r,
bytes32 _s
)
internal
{
_safeExecutionManagerInteraction(
abi.encodeWithSignature(
"ovmCREATEEOA(bytes32,uint8,bytes32,bytes32)",
_messageHash,
_v,
_r,
_s
)
);
}
/**
* Calls the ovmL1TXORIGIN opcode.
* @return Address that sent this message from L1.
*/
function ovmL1TXORIGIN()
internal
returns (
address
)
{
bytes memory returndata = _safeExecutionManagerInteraction(
abi.encodeWithSignature(
"ovmL1TXORIGIN()"
)
);
return abi.decode(returndata, (address));
}
/*********************
* Private Functions *
*********************/
/**
* Performs an ovm interaction and the necessary safety checks.
* @param _calldata Data to send to the OVM_ExecutionManager (encoded with sighash).
* @return _returndata Data sent back by the OVM_ExecutionManager.
*/
function _safeExecutionManagerInteraction(
bytes memory _calldata
)
private
returns (
bytes memory
)
{
bytes memory returndata;
assembly {
// kall is a custom yul builtin within optimistic-solc that allows us to directly call
// the execution manager (since `call` would be compiled).
kall(add(_calldata, 0x20), mload(_calldata), 0x0, 0x0)
let size := returndatasize()
returndata := mload(0x40)
mstore(0x40, add(returndata, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(returndata, size)
returndatacopy(add(returndata, 0x20), 0x0, size)
}
return returndata;
}
}
...@@ -48,13 +48,17 @@ contract TestLib_OVMCodec { ...@@ -48,13 +48,17 @@ contract TestLib_OVMCodec {
} }
function decompressEIP155Transaction( function decompressEIP155Transaction(
bytes memory _transaction bytes memory _transaction,
uint256 _chainId
) )
public public
returns ( returns (
Lib_OVMCodec.EIP155Transaction memory _decompressed Lib_OVMCodec.EIP155Transaction memory _decompressed
) )
{ {
return Lib_OVMCodec.decompressEIP155Transaction(_transaction); return Lib_OVMCodec.decompressEIP155Transaction(
_transaction,
_chainId
);
} }
} }
...@@ -48,6 +48,9 @@ const config: HardhatUserConfig = { ...@@ -48,6 +48,9 @@ const config: HardhatUserConfig = {
}, },
}, },
}, },
ovm: {
solcVersion: '0.7.6-allow_kall_2', // temporary until we fix the build for 0.7.6
},
typechain: { typechain: {
outDir: 'dist/types', outDir: 'dist/types',
target: 'ethers-v5', target: 'ethers-v5',
......
...@@ -220,7 +220,7 @@ export const makeContractDeployConfig = async ( ...@@ -220,7 +220,7 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_ECDSAContractAccount'), factory: getContractFactory('OVM_ECDSAContractAccount'),
}, },
OVM_SequencerEntrypoint: { OVM_SequencerEntrypoint: {
factory: getContractFactory('OVM_SequencerEntrypoint'), factory: getContractFactory('OVM_SequencerEntrypoint', undefined, true),
}, },
OVM_ProxySequencerEntrypoint: { OVM_ProxySequencerEntrypoint: {
factory: getContractFactory('OVM_ProxySequencerEntrypoint'), factory: getContractFactory('OVM_ProxySequencerEntrypoint'),
......
...@@ -164,6 +164,7 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => { ...@@ -164,6 +164,7 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => {
const ovmCompiled = [ const ovmCompiled = [
'OVM_L2ToL1MessagePasser', 'OVM_L2ToL1MessagePasser',
'OVM_L2CrossDomainMessenger', 'OVM_L2CrossDomainMessenger',
'OVM_SequencerEntrypoint',
'Lib_AddressManager', 'Lib_AddressManager',
'OVM_ETH', 'OVM_ETH',
] ]
...@@ -211,12 +212,19 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => { ...@@ -211,12 +212,19 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => {
predeploys[name] || predeploys[name] ||
`0xdeaddeaddeaddeaddeaddeaddeaddeaddead${i.toString(16).padStart(4, '0')}` `0xdeaddeaddeaddeaddeaddeaddeaddeaddead${i.toString(16).padStart(4, '0')}`
let def: any
try {
def = getContractDefinition(name.replace('Proxy__', ''))
} catch (err) {
def = getContractDefinition(name.replace('Proxy__', ''), true)
}
dump.accounts[name] = { dump.accounts[name] = {
address: deadAddress, address: deadAddress,
code, code,
codeHash: keccak256(code), codeHash: keccak256(code),
storage: await getStorageDump(cStateManager, contract.address), storage: await getStorageDump(cStateManager, contract.address),
abi: getContractDefinition(name.replace('Proxy__', '')).abi, abi: def.abi,
} }
} }
......
...@@ -8,6 +8,7 @@ import { remove0x } from '@eth-optimism/core-utils' ...@@ -8,6 +8,7 @@ import { remove0x } from '@eth-optimism/core-utils'
/* Internal Imports */ /* Internal Imports */
import { decodeSolidityError } from '../../../helpers' import { decodeSolidityError } from '../../../helpers'
import { getContractFactory } from '../../../../src'
const callPredeploy = async ( const callPredeploy = async (
Helper_PredeployCaller: Contract, Helper_PredeployCaller: Contract,
...@@ -59,8 +60,10 @@ describe('OVM_ProxySequencerEntrypoint', () => { ...@@ -59,8 +60,10 @@ describe('OVM_ProxySequencerEntrypoint', () => {
Helper_PredeployCaller.setTarget(Mock__OVM_ExecutionManager.address) Helper_PredeployCaller.setTarget(Mock__OVM_ExecutionManager.address)
OVM_SequencerEntrypoint = await ( OVM_SequencerEntrypoint = await getContractFactory(
await ethers.getContractFactory('OVM_SequencerEntrypoint') 'OVM_SequencerEntrypoint',
wallet,
true
).deploy() ).deploy()
}) })
......
...@@ -2,11 +2,12 @@ import { expect } from '../../../setup' ...@@ -2,11 +2,12 @@ import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { waffle, ethers } from 'hardhat' import { waffle, ethers } from 'hardhat'
import { ContractFactory, Wallet, Contract } from 'ethers' import { ContractFactory, Wallet, Contract, BigNumber } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock' import { smockit, MockContract } from '@eth-optimism/smock'
import { fromHexString, toHexString } from '@eth-optimism/core-utils'
/* Internal Imports */ /* Internal Imports */
import { getContractInterface } from '../../../../src' import { getContractInterface, getContractFactory } from '../../../../src'
import { import {
encodeSequencerCalldata, encodeSequencerCalldata,
signNativeTransaction, signNativeTransaction,
...@@ -31,7 +32,38 @@ describe('OVM_SequencerEntrypoint', () => { ...@@ -31,7 +32,38 @@ describe('OVM_SequencerEntrypoint', () => {
) )
Mock__OVM_ExecutionManager.smocked.ovmCHAINID.will.return.with(420) Mock__OVM_ExecutionManager.smocked.ovmCHAINID.will.return.with(420)
Mock__OVM_ExecutionManager.smocked.ovmCALL.will.return.with([true, '0x']) Mock__OVM_ExecutionManager.smocked.ovmCALL.will.return.with(
(gasLimit, target, data) => {
if (target === wallet.address) {
return [
true,
iOVM_ECDSAContractAccount.encodeFunctionResult('execute', [
true,
'0x',
]),
]
} else {
return [true, '0x']
}
}
)
Mock__OVM_ExecutionManager.smocked.ovmSTATICCALL.will.return.with(
(gasLimit, target, data) => {
// Duplicating the behavior of the ecrecover precompile.
if (target === '0x0000000000000000000000000000000000000001') {
const databuf = fromHexString(data)
const addr = ethers.utils.recoverAddress(databuf.slice(0, 32), {
v: BigNumber.from(databuf.slice(32, 64)).toNumber(),
r: toHexString(databuf.slice(64, 96)),
s: toHexString(databuf.slice(96, 128)),
})
const ret = ethers.utils.defaultAbiCoder.encode(['address'], [addr])
return [true, ret]
} else {
return [true, '0x']
}
}
)
Helper_PredeployCaller = await ( Helper_PredeployCaller = await (
await ethers.getContractFactory('Helper_PredeployCaller') await ethers.getContractFactory('Helper_PredeployCaller')
...@@ -42,11 +74,18 @@ describe('OVM_SequencerEntrypoint', () => { ...@@ -42,11 +74,18 @@ describe('OVM_SequencerEntrypoint', () => {
let OVM_SequencerEntrypointFactory: ContractFactory let OVM_SequencerEntrypointFactory: ContractFactory
before(async () => { before(async () => {
OVM_SequencerEntrypointFactory = await ethers.getContractFactory( OVM_SequencerEntrypointFactory = getContractFactory(
'OVM_SequencerEntrypoint' 'OVM_SequencerEntrypoint',
wallet,
true
) )
}) })
const iOVM_ECDSAContractAccount = getContractInterface(
'OVM_ECDSAContractAccount',
true
)
let OVM_SequencerEntrypoint: Contract let OVM_SequencerEntrypoint: Contract
beforeEach(async () => { beforeEach(async () => {
OVM_SequencerEntrypoint = await OVM_SequencerEntrypointFactory.deploy() OVM_SequencerEntrypoint = await OVM_SequencerEntrypointFactory.deploy()
...@@ -69,15 +108,16 @@ describe('OVM_SequencerEntrypoint', () => { ...@@ -69,15 +108,16 @@ describe('OVM_SequencerEntrypoint', () => {
const encodedTx = serializeNativeTransaction(DEFAULT_EIP155_TX) const encodedTx = serializeNativeTransaction(DEFAULT_EIP155_TX)
const sig = await signNativeTransaction(wallet, DEFAULT_EIP155_TX) const sig = await signNativeTransaction(wallet, DEFAULT_EIP155_TX)
const expectedEOACalldata = getContractInterface( const expectedEOACalldata = iOVM_ECDSAContractAccount.encodeFunctionData(
'OVM_ECDSAContractAccount' 'execute',
).encodeFunctionData('execute', [ [
encodedTx, encodedTx,
0, //isEthSignedMessage 0, //isEthSignedMessage
`0x${sig.v}`, //v `0x${sig.v}`, //v
`0x${sig.r}`, //r `0x${sig.r}`, //r
`0x${sig.s}`, //s `0x${sig.s}`, //s
]) ]
)
const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[0] const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[0]
expect(ovmCALL._address).to.equal(await wallet.getAddress()) expect(ovmCALL._address).to.equal(await wallet.getAddress())
expect(ovmCALL._calldata).to.equal(expectedEOACalldata) expect(ovmCALL._calldata).to.equal(expectedEOACalldata)
...@@ -94,15 +134,16 @@ describe('OVM_SequencerEntrypoint', () => { ...@@ -94,15 +134,16 @@ describe('OVM_SequencerEntrypoint', () => {
const encodedTx = serializeNativeTransaction(createTx) const encodedTx = serializeNativeTransaction(createTx)
const sig = await signNativeTransaction(wallet, createTx) const sig = await signNativeTransaction(wallet, createTx)
const expectedEOACalldata = getContractInterface( const expectedEOACalldata = iOVM_ECDSAContractAccount.encodeFunctionData(
'OVM_ECDSAContractAccount' 'execute',
).encodeFunctionData('execute', [ [
encodedTx, encodedTx,
0, //isEthSignedMessage 0, //isEthSignedMessage
`0x${sig.v}`, //v `0x${sig.v}`, //v
`0x${sig.r}`, //r `0x${sig.r}`, //r
`0x${sig.s}`, //s `0x${sig.s}`, //s
]) ]
)
const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[0] const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[0]
expect(ovmCALL._address).to.equal(await wallet.getAddress()) expect(ovmCALL._address).to.equal(await wallet.getAddress())
expect(ovmCALL._calldata).to.equal(expectedEOACalldata) expect(ovmCALL._calldata).to.equal(expectedEOACalldata)
...@@ -110,7 +151,17 @@ describe('OVM_SequencerEntrypoint', () => { ...@@ -110,7 +151,17 @@ describe('OVM_SequencerEntrypoint', () => {
for (let i = 0; i < 3; i += 2) { for (let i = 0; i < 3; i += 2) {
it(`should call ovmCreateEOA when tx type is ${i} and ovmEXTCODESIZE returns 0`, async () => { it(`should call ovmCreateEOA when tx type is ${i} and ovmEXTCODESIZE returns 0`, async () => {
Mock__OVM_ExecutionManager.smocked.ovmEXTCODESIZE.will.return.with(0) let firstCheck = true
Mock__OVM_ExecutionManager.smocked.ovmEXTCODESIZE.will.return.with(
() => {
if (firstCheck) {
firstCheck = false
return 0
} else {
return 1
}
}
)
const calldata = await encodeSequencerCalldata( const calldata = await encodeSequencerCalldata(
wallet, wallet,
DEFAULT_EIP155_TX, DEFAULT_EIP155_TX,
...@@ -145,15 +196,16 @@ describe('OVM_SequencerEntrypoint', () => { ...@@ -145,15 +196,16 @@ describe('OVM_SequencerEntrypoint', () => {
const encodedTx = serializeEthSignTransaction(DEFAULT_EIP155_TX) const encodedTx = serializeEthSignTransaction(DEFAULT_EIP155_TX)
const sig = await signEthSignMessage(wallet, DEFAULT_EIP155_TX) const sig = await signEthSignMessage(wallet, DEFAULT_EIP155_TX)
const expectedEOACalldata = getContractInterface( const expectedEOACalldata = iOVM_ECDSAContractAccount.encodeFunctionData(
'OVM_ECDSAContractAccount' 'execute',
).encodeFunctionData('execute', [ [
encodedTx, encodedTx,
1, //isEthSignedMessage 1, //isEthSignedMessage
`0x${sig.v}`, //v `0x${sig.v}`, //v
`0x${sig.r}`, //r `0x${sig.r}`, //r
`0x${sig.s}`, //s `0x${sig.s}`, //s
]) ]
)
const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[0] const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[0]
expect(ovmCALL._address).to.equal(await wallet.getAddress()) expect(ovmCALL._address).to.equal(await wallet.getAddress())
expect(ovmCALL._calldata).to.equal(expectedEOACalldata) expect(ovmCALL._calldata).to.equal(expectedEOACalldata)
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
"decompressEIP155Transaction": { "decompressEIP155Transaction": {
"decompression": { "decompression": {
"in": [ "in": [
"0x0001f4000064000064121212121212121212121212121212121212121299999999999999999999" "0x0001f4000064000064121212121212121212121212121212121212121299999999999999999999",
420
], ],
"out": [ "out": [
[ [
......
...@@ -13,6 +13,9 @@ import { ...@@ -13,6 +13,9 @@ import {
/* Imports: Internal */ /* Imports: Internal */
import './type-extensions' import './type-extensions'
const OPTIMISM_SOLC_VERSION_URL =
'https://api.github.com/repos/ethereum-optimism/solc-bin/git/refs/heads/gh-pages'
const OPTIMISM_SOLC_BIN_URL = const OPTIMISM_SOLC_BIN_URL =
'https://raw.githubusercontent.com/ethereum-optimism/solc-bin/gh-pages/bin' 'https://raw.githubusercontent.com/ethereum-optimism/solc-bin/gh-pages/bin'
...@@ -41,10 +44,32 @@ const getOvmSolcPath = async (version: string): Promise<string> => { ...@@ -41,10 +44,32 @@ const getOvmSolcPath = async (version: string): Promise<string> => {
if (!fs.existsSync(ovmCompilersCache)) if (!fs.existsSync(ovmCompilersCache))
[fs.mkdirSync(ovmCompilersCache, { recursive: true })] [fs.mkdirSync(ovmCompilersCache, { recursive: true })]
// Pull information about the latest commit in the solc-bin repo. We'll use this to invalidate
// our compiler cache if necessary.
const remoteCompilerVersion = await (
await fetch(OPTIMISM_SOLC_VERSION_URL)
).text()
// Pull the locally stored info about the latest commit. If this differs from the remote info
// then we know to invalidate our cache.
let cachedCompilerVersion = ''
const cachedCompilerVersionPath = path.join(
ovmCompilersCache,
`version-info-${version}.json`
)
if (fs.existsSync(cachedCompilerVersionPath)) {
cachedCompilerVersion = fs
.readFileSync(cachedCompilerVersionPath)
.toString()
}
// Check to see if we already have this compiler version downloaded. We store the cached files at // Check to see if we already have this compiler version downloaded. We store the cached files at
// `X.Y.Z.js`. If it already exists, just return that instead of downloading a new one. // `X.Y.Z.js`. If it already exists, just return that instead of downloading a new one.
const cachedCompilerPath = path.join(ovmCompilersCache, `${version}.js`) const cachedCompilerPath = path.join(ovmCompilersCache, `${version}.js`)
if (fs.existsSync(cachedCompilerPath)) { if (
remoteCompilerVersion === cachedCompilerVersion &&
fs.existsSync(cachedCompilerPath)
) {
return cachedCompilerPath return cachedCompilerPath
} }
...@@ -68,6 +93,7 @@ const getOvmSolcPath = async (version: string): Promise<string> => { ...@@ -68,6 +93,7 @@ const getOvmSolcPath = async (version: string): Promise<string> => {
// figure out how to properly extend and/or hack Hardat's CompilerDownloader class. // figure out how to properly extend and/or hack Hardat's CompilerDownloader class.
const compilerContent = await compilerContentResponse.text() const compilerContent = await compilerContentResponse.text()
fs.writeFileSync(cachedCompilerPath, compilerContent) fs.writeFileSync(cachedCompilerPath, compilerContent)
fs.writeFileSync(cachedCompilerVersionPath, remoteCompilerVersion)
return cachedCompilerPath return cachedCompilerPath
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment