Skip to content

Commit

Permalink
Adds an ovm compilation plugin, compiles into artifacts and artifacts…
Browse files Browse the repository at this point in the history
…/ovm (#118)

* Linted files

* Revert back to old artifacts structure

* Fixed conflict with smock
  • Loading branch information
smartcontracts authored Dec 11, 2020
1 parent 87d1b3c commit d068cc8
Show file tree
Hide file tree
Showing 16 changed files with 302 additions and 23 deletions.
2 changes: 1 addition & 1 deletion packages/contracts/buidler.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ usePlugin('@nomiclabs/buidler-ethers')
usePlugin('@nomiclabs/buidler-waffle')
usePlugin('buidler-typechain')

import '@eth-optimism/smock/build/src/buidler-plugins/compiler-storage-layout'
import './plugins/buidler/ovm-compiler'

const config: BuidlerConfig = {
networks: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;

/* Interface Imports */
import { iOVM_BaseCrossDomainMessenger } from "../../iOVM/bridge/iOVM_BaseCrossDomainMessenger.sol";
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';

/* Library Imports */
import { Lib_ReentrancyGuard } from "../../libraries/utils/Lib_ReentrancyGuard.sol";

/**
* @title OVM_BaseCrossDomainMessenger
*/
contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger, ReentrancyGuard {
contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger, Lib_ReentrancyGuard {

/**********************
* Contract Variables *
Expand All @@ -25,7 +28,7 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger, Reentran
* Public Functions *
********************/

constructor() ReentrancyGuard() public {}
constructor() Lib_ReentrancyGuard() public {}

/**
* Sends a cross domain message to the target messenger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;

Expand Down Expand Up @@ -28,7 +29,10 @@ contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCros
*/
constructor(
address _libAddressManager
) Lib_AddressResolver(_libAddressManager) {}
)
public
Lib_AddressResolver(_libAddressManager)
{}


/********************
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;

/* Interface Imports */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;

/**
* @title iOVM_L1MessageSender
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;

/**
* @title iOVM_L2ToL1MessagePasser
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;

/* Contract Imports */
import { Ownable } from "./Lib_Ownable.sol";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;

/* Library Imports */
import { Lib_AddressManager } from "./Lib_AddressManager.sol";
Expand All @@ -25,7 +26,7 @@ contract Lib_AddressResolver {
*/
constructor(
address _libAddressManager
) {
) public {
libAddressManager = Lib_AddressManager(_libAddressManager);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;

/**
* @title Ownable
Expand Down Expand Up @@ -28,7 +29,7 @@ abstract contract Ownable {
* Constructor *
***************/

constructor() {
constructor() internal {
owner = msg.sender;
emit OwnershipTransferred(address(0), owner);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
// +build ovm
pragma solidity >0.6.0 <0.8.0;

/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract Lib_ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.

// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;

uint256 private _status;

constructor () internal {
_status = _NOT_ENTERED;
}

/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

// Any calls to nonReentrant after this point will fail
_status = _ENTERED;

_;

// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
3 changes: 2 additions & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
"test:gas": "buidler test \"test/contracts/OVM/execution/OVM_StateManager.gas-spec.ts\" --no-compile --show-stack-traces",
"lint": "yarn run lint:typescript",
"lint:typescript": "tslint --format stylish --project .",
"lint:fix:typescript": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\"",
"lint:fix:typescript": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test,plugins}/**/*.ts\"",
"clean": "rm -rf ./artifacts ./build ./cache",
"deploy": "./bin/deploy.js"
},
"dependencies": {
"@eth-optimism/solc": "^0.6.12-alpha.1",
"@ethersproject/contracts": "^5.0.5",
"@ethersproject/hardware-wallets": "^5.0.8",
"@openzeppelin/contracts": "^3.3.0",
Expand Down
173 changes: 173 additions & 0 deletions packages/contracts/plugins/buidler/ovm-compiler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import * as path from 'path'
import fsExtra from 'fs-extra'
import { internalTask } from '@nomiclabs/buidler/config'
import { SolcInput } from '@nomiclabs/buidler/types'
import { Compiler } from '@nomiclabs/buidler/internal/solidity/compiler'
import { pluralize } from '@nomiclabs/buidler/internal/util/strings'
import {
saveArtifact,
getArtifactFromContractOutput,
} from '@nomiclabs/buidler/internal/artifacts'
import {
TASK_COMPILE_RUN_COMPILER,
TASK_BUILD_ARTIFACTS,
TASK_COMPILE_GET_SOURCE_PATHS,
TASK_COMPILE_CHECK_CACHE,
TASK_COMPILE_COMPILE,
TASK_COMPILE_GET_COMPILER_INPUT,
} from '@nomiclabs/buidler/builtin-tasks/task-names'

internalTask(TASK_COMPILE_RUN_COMPILER).setAction(
async ({ input }: { input: SolcInput }, { config }) => {
// Try to find a path to @eth-optimism/solc, throw if we can't.
let ovmSolcJs: any
try {
ovmSolcJs = require('@eth-optimism/solc')
} catch (err) {
if (err.toString().contains('Cannot find module')) {
throw new Error(
`ovm-toolchain: Could not find "@eth-optimism/solc" in your node_modules.`
)
} else {
throw err
}
}

const evmCompiler = new Compiler(
config.solc.version,
path.join(config.paths.cache, 'compilers')
)

const ovmCompiler = new Compiler(
ovmSolcJs.version(),
path.join(config.paths.cache, 'compilers')
)

ovmCompiler.getSolc = () => {
return ovmSolcJs
}

const ovmInput = {
language: 'Solidity',
sources: {},
settings: input.settings,
}
const evmInput = {
language: 'Solidity',
sources: {},
settings: input.settings,
}

// Separate the EVM and OVM inputs.
for (const file of Object.keys(input.sources)) {
evmInput.sources[file] = input.sources[file]

if (input.sources[file].content.includes('// +build ovm')) {
ovmInput.sources[file] = input.sources[file]
}
}

// Build both inputs separately.
console.log('Compiling ovm contracts...')
const ovmOutput = await ovmCompiler.compile(ovmInput)
console.log('Compiling evm contracts...')
const evmOutput = await evmCompiler.compile(evmInput)

// Filter out any "No input sources specified" errors, but only if one of the two compilations
// threw the error.
let errors = (ovmOutput.errors || []).concat(evmOutput.errors || [])
const filtered = errors.filter((error: any) => {
return error.message !== 'No input sources specified.'
})
if (errors.length === filtered.length + 1) {
errors = filtered
}

for (const name of Object.keys(ovmOutput.contracts)) {
ovmOutput.contracts[`${name}.ovm`] = ovmOutput.contracts[name]
delete ovmOutput.contracts[name]
}

// Combine the outputs.
const output = {
contracts: {
...ovmOutput.contracts,
...evmOutput.contracts,
},
errors,
sources: {
...ovmOutput.sources,
...evmOutput.sources,
},
}

return output
}
)

internalTask(
TASK_COMPILE_GET_COMPILER_INPUT,
async (_, { config, run }, runSuper) => {
const input = await runSuper()

// For smock.
input.settings.outputSelection['*']['*'].push('storageLayout')

return input
}
)

internalTask(TASK_BUILD_ARTIFACTS, async ({ force }, { config, run }) => {
const sources = await run(TASK_COMPILE_GET_SOURCE_PATHS)

if (sources.length === 0) {
console.log('No Solidity source file available.')
return
}

const isCached: boolean = await run(TASK_COMPILE_CHECK_CACHE, { force })

if (isCached) {
console.log(
'All contracts have already been compiled, skipping compilation.'
)
return
}

const compilationOutput = await run(TASK_COMPILE_COMPILE)

if (compilationOutput === undefined) {
return
}

await fsExtra.ensureDir(config.paths.artifacts)
let numberOfContracts = 0

for (const [fileName, file] of Object.entries<any>(
compilationOutput.contracts
)) {
for (const [contractName, contractOutput] of Object.entries(file)) {
const artifact = getArtifactFromContractOutput(
contractName,
contractOutput
)
numberOfContracts += 1

// For smock.
;(artifact as any).storageLayout = (contractOutput as any).storageLayout

if (fileName.endsWith('.ovm')) {
await saveArtifact(config.paths.artifacts + '/ovm', artifact)
} else {
await saveArtifact(config.paths.artifacts, artifact)
}
}
}

console.log(
'Compiled',
numberOfContracts,
pluralize(numberOfContracts, 'contract'),
'successfully'
)
})
Loading

0 comments on commit d068cc8

Please sign in to comment.