Commit 0f8bac35 authored by Georgios Konstantopoulos's avatar Georgios Konstantopoulos Committed by GitHub

chore: update smock and contracts to latest master (#52)

* fix(smock): update to latest master

https://github.com/ethereum-optimism/smock/pull/43
https://github.com/ethereum-optimism/smock/pull/40
https://github.com/ethereum-optimism/smock/pull/37
https://github.com/ethereum-optimism/smock/pull/36

* fix(contracts): revert more rigorously for unsafe bytecode

https://github.com/ethereum-optimism/contracts/pull/369

* fix(contracts): prevent re-entrancy by disallowing default context values

https://github.com/ethereum-optimism/contracts/pull/363

* test(contracts): improve coverage for StateTransitionerFactory

https://github.com/ethereum-optimism/contracts/pull/365

* chore(contracts): cleanup libraries

* fix: MAX_ROLLUP_TX_SIZE = 50k

* test: fix batch submitter scc import path

* fix(contracts): set correct nuisance gas

We get this by inspecting the EM.messageRecord.nuisanceGasLeft storage slot manually, would be nice
if we could automatically calculate this
parent 0746c1ac
...@@ -7,8 +7,9 @@ import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' ...@@ -7,8 +7,9 @@ import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import ganache from 'ganache-core' import ganache from 'ganache-core'
import sinon from 'sinon' import sinon from 'sinon'
import { Web3Provider } from '@ethersproject/providers' import { Web3Provider } from '@ethersproject/providers'
import scc from '@eth-optimism/contracts/artifacts/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol/OVM_StateCommitmentChain.json'
import { getContractInterface } from '@eth-optimism/contracts' import { getContractInterface } from '@eth-optimism/contracts'
import * as scc from '@eth-optimism/contracts/dist/artifacts/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol/OVM_StateCommitmentChain.json'
import { smockit, MockContract } from '@eth-optimism/smock' import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */ /* Internal Imports */
......
...@@ -38,7 +38,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -38,7 +38,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
// L2 tx gas-related // L2 tx gas-related
uint256 constant public MIN_ROLLUP_TX_GAS = 100000; uint256 constant public MIN_ROLLUP_TX_GAS = 100000;
uint256 constant public MAX_ROLLUP_TX_SIZE = 10000; uint256 constant public MAX_ROLLUP_TX_SIZE = 50000;
uint256 constant public L2_GAS_DISCOUNT_DIVISOR = 32; uint256 constant public L2_GAS_DISCOUNT_DIVISOR = 32;
// Encoding-related (all in bytes) // Encoding-related (all in bytes)
......
...@@ -67,6 +67,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -67,6 +67,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
uint256 constant NUISANCE_GAS_PER_CONTRACT_BYTE = 100; uint256 constant NUISANCE_GAS_PER_CONTRACT_BYTE = 100;
uint256 constant MIN_GAS_FOR_INVALID_STATE_ACCESS = 30000; uint256 constant MIN_GAS_FOR_INVALID_STATE_ACCESS = 30000;
/**************************
* Default Context Values *
**************************/
uint256 constant DEFAULT_UINT256 = 0xdefa017defa017defa017defa017defa017defa017defa017defa017defa017d;
address constant DEFAULT_ADDRESS = 0xdEfa017defA017DeFA017DEfa017DeFA017DeFa0;
/*************** /***************
* Constructor * * Constructor *
...@@ -159,7 +165,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -159,7 +165,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
override override
public public
{ {
require(transactionContext.ovmNUMBER == 0, "Only callable at the start of a transaction"); // Make sure that run() is not re-enterable. This condition should always be satisfied
// Once run has been called once, due to the behavior of _isValidInput().
if (transactionContext.ovmNUMBER != DEFAULT_UINT256) {
return;
}
// Store our OVM_StateManager instance (significantly easier than attempting to pass the // Store our OVM_StateManager instance (significantly easier than attempting to pass the
// address around in calldata). // address around in calldata).
ovmStateManager = iOVM_StateManager(_ovmStateManager); ovmStateManager = iOVM_StateManager(_ovmStateManager);
...@@ -183,7 +194,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -183,7 +194,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// Make sure the transaction's gas limit is valid. We don't revert here because we reserve // Make sure the transaction's gas limit is valid. We don't revert here because we reserve
// reverts for INVALID_STATE_ACCESS. // reverts for INVALID_STATE_ACCESS.
if (_isValidGasLimit(_transaction.gasLimit, _transaction.l1QueueOrigin) == false) { if (_isValidInput(_transaction) == false) {
_resetContext(); _resetContext();
return; return;
} }
...@@ -952,10 +963,23 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -952,10 +963,23 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// revert data as to retrieve execution metadata that would normally be reverted out of // revert data as to retrieve execution metadata that would normally be reverted out of
// existence. // existence.
(bool success, bytes memory returndata) = bool success;
_isCreate bytes memory returndata;
? _handleContractCreation(_gasLimit, _data, _contract) if (_isCreate) {
: _contract.call{gas: _gasLimit}(_data); // safeCREATE() is a function which replicates a CREATE message, but uses return values
// Which match that of CALL (i.e. bool, bytes). This allows many security checks to be
// to be shared between untrusted call and create call frames.
(success, returndata) = address(this).call(
abi.encodeWithSelector(
this.safeCREATE.selector,
_gasLimit,
_data,
_contract
)
);
} else {
(success, returndata) = _contract.call{gas: _gasLimit}(_data);
}
// Switch back to the original message context now that we're out of the call. // Switch back to the original message context now that we're out of the call.
_switchMessageContext(_nextMessageContext, prevMessageContext); _switchMessageContext(_nextMessageContext, prevMessageContext);
...@@ -1023,83 +1047,69 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1023,83 +1047,69 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
/** /**
* Handles the creation-specific safety measures required for OVM contract deployment. * Handles the creation-specific safety measures required for OVM contract deployment.
* This function sanitizes the return types for creation messages to match calls (bool, bytes). * This function sanitizes the return types for creation messages to match calls (bool, bytes),
* by being an external function which the EM can call, that mimics the success/fail case of the CREATE.
* This allows for consistent handling of both types of messages in _handleExternalMessage(). * This allows for consistent handling of both types of messages in _handleExternalMessage().
* * Having this step occur as a separate call frame also allows us to easily revert the
* contract deployment in the event that the code is unsafe.
*
* @param _gasLimit Amount of gas to be passed into this creation. * @param _gasLimit Amount of gas to be passed into this creation.
* @param _creationCode Code to pass into CREATE for deployment. * @param _creationCode Code to pass into CREATE for deployment.
* @param _address OVM address being deployed to. * @param _address OVM address being deployed to.
* @return Whether or not the call succeeded.
* @return If creation fails: revert data. Otherwise: empty.
*/ */
function _handleContractCreation( function safeCREATE(
uint _gasLimit, uint _gasLimit,
bytes memory _creationCode, bytes memory _creationCode,
address _address address _address
) )
internal external
returns(
bool,
bytes memory
)
{ {
// The only way this should callable is from within _createContract(),
// and it should DEFINITELY not be callable by a non-EM code contract.
if (msg.sender != address(this)) {
return;
}
// Check that there is not already code at this address. // Check that there is not already code at this address.
if (_hasEmptyAccount(_address) == false) { if (_hasEmptyAccount(_address) == false) {
// Note: in the EVM, this case burns all allotted gas. For improved // Note: in the EVM, this case burns all allotted gas. For improved
// developer experience, we do return the remaining ones. // developer experience, we do return the remaining gas.
return ( _revertWithFlag(
false, RevertFlag.CREATE_COLLISION,
_encodeRevertData( Lib_ErrorUtils.encodeRevertString("A contract has already been deployed to this address")
RevertFlag.CREATE_COLLISION,
Lib_ErrorUtils.encodeRevertString("A contract has already been deployed to this address")
)
); );
} }
// Check the creation bytecode against the OVM_SafetyChecker. // Check the creation bytecode against the OVM_SafetyChecker.
if (ovmSafetyChecker.isBytecodeSafe(_creationCode) == false) { if (ovmSafetyChecker.isBytecodeSafe(_creationCode) == false) {
return ( _revertWithFlag(
false, RevertFlag.UNSAFE_BYTECODE,
_encodeRevertData( Lib_ErrorUtils.encodeRevertString("Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?")
RevertFlag.UNSAFE_BYTECODE,
Lib_ErrorUtils.encodeRevertString("Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?")
)
); );
} }
// We always need to initialize the contract with the default account values. // We always need to initialize the contract with the default account values.
_initPendingAccount(_address); _initPendingAccount(_address);
// Actually execute the EVM create message, // Actually execute the EVM create message.
// NOTE: The inline assembly below means we can NOT make any evm calls between here and then.
address ethAddress = Lib_EthUtils.createContract(_creationCode); address ethAddress = Lib_EthUtils.createContract(_creationCode);
if (ethAddress == address(0)) { if (ethAddress == address(0)) {
// If the creation fails, the EVM lets us grab its revert data. This may contain a revert flag // If the creation fails, the EVM lets us grab its revert data. This may contain a revert flag
// to be used above in _handleExternalMessage. // to be used above in _handleExternalMessage, so we pass the revert data back up unmodified.
uint256 revertDataSize; assembly {
assembly { revertDataSize := returndatasize() } returndatacopy(0,0,returndatasize())
bytes memory revertdata = new bytes(revertDataSize); revert(0, returndatasize())
assembly {
returndatacopy(
add(revertdata, 0x20),
0,
revertDataSize
)
} }
// Return that the creation failed, and the data it reverted with.
return (false, revertdata);
} }
// Again simply checking that the deployed code is safe too. Contracts can generate // Again simply checking that the deployed code is safe too. Contracts can generate
// arbitrary deployment code, so there's no easy way to analyze this beforehand. // arbitrary deployment code, so there's no easy way to analyze this beforehand.
bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress); bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress);
if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) { if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) {
return ( _revertWithFlag(
false, RevertFlag.UNSAFE_BYTECODE,
_encodeRevertData( Lib_ErrorUtils.encodeRevertString("Constructor attempted to deploy unsafe bytecode.")
RevertFlag.UNSAFE_BYTECODE,
Lib_ErrorUtils.encodeRevertString("Constructor attempted to deploy unsafe bytecode.")
)
); );
} }
...@@ -1110,9 +1120,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1110,9 +1120,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
ethAddress, ethAddress,
Lib_EthUtils.getCodeHash(ethAddress) Lib_EthUtils.getCodeHash(ethAddress)
); );
// Successful deployments will not give access to returndata, in both the EVM and the OVM.
return (true, hex'');
} }
/****************************************** /******************************************
...@@ -1626,6 +1633,37 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1626,6 +1633,37 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
} }
} }
/**
* Validates the input values of a transaction.
* @return _valid Whether or not the transaction data is valid.
*/
function _isValidInput(
Lib_OVMCodec.Transaction memory _transaction
)
view
internal
returns (
bool
)
{
// Prevent reentrancy to run():
// This check prevents calling run with the default ovmNumber.
// Combined with the first check in run():
// if (transactionContext.ovmNUMBER != DEFAULT_UINT256) { return; }
// It should be impossible to re-enter since run() returns before any other call frames are created.
// Since this value is already being written to storage, we save much gas compared to
// using the standard nonReentrant pattern.
if (_transaction.blockNumber == DEFAULT_UINT256) {
return false;
}
if (_isValidGasLimit(_transaction.gasLimit, _transaction.l1QueueOrigin) == false) {
return false;
}
return true;
}
/** /**
* Validates the gas limit for a given transaction. * Validates the gas limit for a given transaction.
* @param _gasLimit Gas limit provided by the transaction. * @param _gasLimit Gas limit provided by the transaction.
...@@ -1796,20 +1834,20 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1796,20 +1834,20 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
function _resetContext() function _resetContext()
internal internal
{ {
transactionContext.ovmL1TXORIGIN = address(0); transactionContext.ovmL1TXORIGIN = DEFAULT_ADDRESS;
transactionContext.ovmTIMESTAMP = 0; transactionContext.ovmTIMESTAMP = DEFAULT_UINT256;
transactionContext.ovmNUMBER = 0; transactionContext.ovmNUMBER = DEFAULT_UINT256;
transactionContext.ovmGASLIMIT = 0; transactionContext.ovmGASLIMIT = DEFAULT_UINT256;
transactionContext.ovmTXGASLIMIT = 0; transactionContext.ovmTXGASLIMIT = DEFAULT_UINT256;
transactionContext.ovmL1QUEUEORIGIN = Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE; transactionContext.ovmL1QUEUEORIGIN = Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE;
transactionRecord.ovmGasRefund = 0; transactionRecord.ovmGasRefund = DEFAULT_UINT256;
messageContext.ovmCALLER = address(0); messageContext.ovmCALLER = DEFAULT_ADDRESS;
messageContext.ovmADDRESS = address(0); messageContext.ovmADDRESS = DEFAULT_ADDRESS;
messageContext.isStatic = false; messageContext.isStatic = false;
messageRecord.nuisanceGasLeft = 0; messageRecord.nuisanceGasLeft = DEFAULT_UINT256;
// Reset the ovmStateManager. // Reset the ovmStateManager.
ovmStateManager = iOVM_StateManager(address(0)); ovmStateManager = iOVM_StateManager(address(0));
......
...@@ -23,15 +23,20 @@ import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol"; ...@@ -23,15 +23,20 @@ import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol";
*/ */
contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_AddressResolver { contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_AddressResolver {
/***************
* Constructor *
***************/
constructor( constructor(
address _libAddressManager address _libAddressManager
) )
Lib_AddressResolver(_libAddressManager) Lib_AddressResolver(_libAddressManager)
{} {}
/***************************************
* Public Functions: Contract Creation * /********************
***************************************/ * Public Functions *
********************/
/** /**
* Creates a new OVM_StateTransitioner * Creates a new OVM_StateTransitioner
...@@ -39,7 +44,7 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_Addr ...@@ -39,7 +44,7 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_Addr
* @param _stateTransitionIndex Index of the state transition being verified. * @param _stateTransitionIndex Index of the state transition being verified.
* @param _preStateRoot State root before the transition was executed. * @param _preStateRoot State root before the transition was executed.
* @param _transactionHash Hash of the executed transaction. * @param _transactionHash Hash of the executed transaction.
* @return _ovmStateTransitioner New OVM_StateTransitioner instance. * @return New OVM_StateTransitioner instance.
*/ */
function create( function create(
address _libAddressManager, address _libAddressManager,
...@@ -50,13 +55,14 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_Addr ...@@ -50,13 +55,14 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_Addr
override override
public public
returns ( returns (
iOVM_StateTransitioner _ovmStateTransitioner iOVM_StateTransitioner
) )
{ {
require( require(
msg.sender == resolve("OVM_FraudVerifier"), msg.sender == resolve("OVM_FraudVerifier"),
"Create can only be done by the OVM_FraudVerifier." "Create can only be done by the OVM_FraudVerifier."
); );
return new OVM_StateTransitioner( return new OVM_StateTransitioner(
_libAddressManager, _libAddressManager,
_stateTransitionIndex, _stateTransitionIndex,
......
...@@ -18,9 +18,10 @@ contract Lib_AddressManager is Ownable { ...@@ -18,9 +18,10 @@ contract Lib_AddressManager is Ownable {
address _newAddress address _newAddress
); );
/*******************************************
* Contract Variables: Internal Accounting * /*************
*******************************************/ * Variables *
*************/
mapping (bytes32 => address) private addresses; mapping (bytes32 => address) private addresses;
...@@ -29,6 +30,11 @@ contract Lib_AddressManager is Ownable { ...@@ -29,6 +30,11 @@ contract Lib_AddressManager is Ownable {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Changes the address associated with a particular name.
* @param _name String name to associate an address with.
* @param _address Address to associate with the name.
*/
function setAddress( function setAddress(
string memory _name, string memory _name,
address _address address _address
...@@ -36,16 +42,27 @@ contract Lib_AddressManager is Ownable { ...@@ -36,16 +42,27 @@ contract Lib_AddressManager is Ownable {
public public
onlyOwner onlyOwner
{ {
emit AddressSet(_name, _address);
addresses[_getNameHash(_name)] = _address; addresses[_getNameHash(_name)] = _address;
emit AddressSet(
_name,
_address
);
} }
/**
* Retrieves the address associated with a given name.
* @param _name Name to retrieve an address for.
* @return Address associated with the given name.
*/
function getAddress( function getAddress(
string memory _name string memory _name
) )
public public
view view
returns (address) returns (
address
)
{ {
return addresses[_getNameHash(_name)]; return addresses[_getNameHash(_name)];
} }
...@@ -55,13 +72,18 @@ contract Lib_AddressManager is Ownable { ...@@ -55,13 +72,18 @@ contract Lib_AddressManager is Ownable {
* Internal Functions * * Internal Functions *
**********************/ **********************/
/**
* Computes the hash of a name.
* @param _name Name to compute a hash for.
* @return Hash of the given name.
*/
function _getNameHash( function _getNameHash(
string memory _name string memory _name
) )
internal internal
pure pure
returns ( returns (
bytes32 _hash bytes32
) )
{ {
return keccak256(abi.encodePacked(_name)); return keccak256(abi.encodePacked(_name));
......
...@@ -9,9 +9,9 @@ import { Lib_AddressManager } from "./Lib_AddressManager.sol"; ...@@ -9,9 +9,9 @@ import { Lib_AddressManager } from "./Lib_AddressManager.sol";
*/ */
abstract contract Lib_AddressResolver { abstract contract Lib_AddressResolver {
/******************************************* /*************
* Contract Variables: Contract References * * Variables *
*******************************************/ *************/
Lib_AddressManager public libAddressManager; Lib_AddressManager public libAddressManager;
...@@ -25,7 +25,7 @@ abstract contract Lib_AddressResolver { ...@@ -25,7 +25,7 @@ abstract contract Lib_AddressResolver {
*/ */
constructor( constructor(
address _libAddressManager address _libAddressManager
) { ) {
libAddressManager = Lib_AddressManager(_libAddressManager); libAddressManager = Lib_AddressManager(_libAddressManager);
} }
...@@ -34,13 +34,18 @@ abstract contract Lib_AddressResolver { ...@@ -34,13 +34,18 @@ abstract contract Lib_AddressResolver {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Resolves the address associated with a given name.
* @param _name Name to resolve an address for.
* @return Address associated with the given name.
*/
function resolve( function resolve(
string memory _name string memory _name
) )
public public
view view
returns ( returns (
address _contract address
) )
{ {
return libAddressManager.getAddress(_name); return libAddressManager.getAddress(_name);
......
...@@ -51,6 +51,10 @@ abstract contract Ownable { ...@@ -51,6 +51,10 @@ abstract contract Ownable {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Sets the owner of this contract to the zero address, effectively renouncing ownership
* completely. Can only be called by the current owner of this contract.
*/
function renounceOwnership() function renounceOwnership()
public public
virtual virtual
...@@ -60,7 +64,14 @@ abstract contract Ownable { ...@@ -60,7 +64,14 @@ abstract contract Ownable {
owner = address(0); owner = address(0);
} }
function transferOwnership(address _newOwner) /**
* Transfers ownership of this contract to a new address. Can only be called by the current
* owner of this contract.
* @param _newOwner Address of the new contract owner.
*/
function transferOwnership(
address _newOwner
)
public public
virtual virtual
onlyOwner onlyOwner
......
...@@ -13,7 +13,6 @@ contract Lib_ResolvedDelegateProxy { ...@@ -13,7 +13,6 @@ contract Lib_ResolvedDelegateProxy {
* Variables * * Variables *
*************/ *************/
// Using mappings to store fields to avoid overwriting storage slots in the // Using mappings to store fields to avoid overwriting storage slots in the
// implementation contract. For example, instead of storing these fields at // implementation contract. For example, instead of storing these fields at
// storage slot `0` & `1`, they are stored at `keccak256(key + slot)`. // storage slot `0` & `1`, they are stored at `keccak256(key + slot)`.
...@@ -22,8 +21,8 @@ contract Lib_ResolvedDelegateProxy { ...@@ -22,8 +21,8 @@ contract Lib_ResolvedDelegateProxy {
// There is a known flaw in this contract, and we will remove it from the repository // There is a known flaw in this contract, and we will remove it from the repository
// in the near future. Due to the very limited way that we are using it, this flaw is // in the near future. Due to the very limited way that we are using it, this flaw is
// not an issue in our system. // not an issue in our system.
mapping(address=>string) private implementationName; mapping (address => string) private implementationName;
mapping(address=>Lib_AddressManager) private addressManager; mapping (address => Lib_AddressManager) private addressManager;
/*************** /***************
...@@ -52,7 +51,10 @@ contract Lib_ResolvedDelegateProxy { ...@@ -52,7 +51,10 @@ contract Lib_ResolvedDelegateProxy {
external external
payable payable
{ {
address target = addressManager[address(this)].getAddress((implementationName[address(this)])); address target = addressManager[address(this)].getAddress(
(implementationName[address(this)])
);
require( require(
target != address(0), target != address(0),
"Target address must be initialized." "Target address must be initialized."
......
...@@ -36,7 +36,7 @@ describe('OVM_L1ERC20Gateway', () => { ...@@ -36,7 +36,7 @@ describe('OVM_L1ERC20Gateway', () => {
L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC') L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
const aliceAddress = await alice.getAddress() const aliceAddress = await alice.getAddress()
L1ERC20.smodify.put({ await L1ERC20.smodify.put({
totalSupply: INITIAL_TOTAL_L1_SUPPLY, totalSupply: INITIAL_TOTAL_L1_SUPPLY,
balanceOf: { balanceOf: {
[aliceAddress]: INITIAL_TOTAL_L1_SUPPLY, [aliceAddress]: INITIAL_TOTAL_L1_SUPPLY,
......
...@@ -188,7 +188,9 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -188,7 +188,9 @@ describe('OVM_CanonicalTransactionChain', () => {
const data = '0x' + '12'.repeat(MAX_ROLLUP_TX_SIZE + 1) const data = '0x' + '12'.repeat(MAX_ROLLUP_TX_SIZE + 1)
await expect( await expect(
OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, {
gasLimit: 40000000,
})
).to.be.revertedWith( ).to.be.revertedWith(
'Transaction data size exceeds maximum for rollup transaction.' 'Transaction data size exceeds maximum for rollup transaction.'
) )
......
...@@ -110,7 +110,7 @@ describe('OVM_ExecutionManager gas consumption', () => { ...@@ -110,7 +110,7 @@ describe('OVM_ExecutionManager gas consumption', () => {
) )
console.log(`calculated gas cost of ${gasCost}`) console.log(`calculated gas cost of ${gasCost}`)
const benchmark: number = 225_500 const benchmark: number = 105_000
expect(gasCost).to.be.lte(benchmark) expect(gasCost).to.be.lte(benchmark)
expect(gasCost).to.be.gte( expect(gasCost).to.be.gte(
benchmark - 1_000, benchmark - 1_000,
......
...@@ -188,7 +188,7 @@ const test_nuisanceGas: TestDefinition = { ...@@ -188,7 +188,7 @@ const test_nuisanceGas: TestDefinition = {
// This is because there is natural gas consumption between the ovmCALL(GAS/2) and ovmCREATE, which allots nuisance gas via _getNuisanceGasLimit. // This is because there is natural gas consumption between the ovmCALL(GAS/2) and ovmCREATE, which allots nuisance gas via _getNuisanceGasLimit.
// This means that the ovmCREATE exception, DOES consumes all nuisance gas allotted, but that allotment // This means that the ovmCREATE exception, DOES consumes all nuisance gas allotted, but that allotment
// is less than the full OVM_TX_GAS_LIMIT / 2 which is alloted to the parent ovmCALL. // is less than the full OVM_TX_GAS_LIMIT / 2 which is alloted to the parent ovmCALL.
nuisanceGasLeft: 4531286, nuisanceGasLeft: 4184829,
}, },
}, },
}, },
......
...@@ -212,7 +212,7 @@ describe('OVM_BondManager', () => { ...@@ -212,7 +212,7 @@ describe('OVM_BondManager', () => {
// smodify the canClaim value to true to test claiming // smodify the canClaim value to true to test claiming
const block = await provider.getBlock('latest') const block = await provider.getBlock('latest')
timestamp = block.timestamp timestamp = block.timestamp
bondManager.smodify.set({ await bondManager.smodify.put({
witnessProviders: { witnessProviders: {
[preStateRoot]: { [preStateRoot]: {
canClaim: true, canClaim: true,
......
...@@ -312,7 +312,7 @@ describe('OVM_StateTransitioner', () => { ...@@ -312,7 +312,7 @@ describe('OVM_StateTransitioner', () => {
) )
) )
OVM_StateTransitioner.smodify.set({ await OVM_StateTransitioner.smodify.put({
phase: 0, phase: 0,
transactionHash, transactionHash,
}) })
...@@ -329,7 +329,7 @@ describe('OVM_StateTransitioner', () => { ...@@ -329,7 +329,7 @@ describe('OVM_StateTransitioner', () => {
describe('commitContractState', () => { describe('commitContractState', () => {
beforeEach(async () => { beforeEach(async () => {
OVM_StateTransitioner.smodify.set({ await OVM_StateTransitioner.smodify.put({
phase: 1, phase: 1,
}) })
}) })
...@@ -394,7 +394,7 @@ describe('OVM_StateTransitioner', () => { ...@@ -394,7 +394,7 @@ describe('OVM_StateTransitioner', () => {
proof = test.accountTrieWitness proof = test.accountTrieWitness
postStateRoot = test.newAccountTrieRoot postStateRoot = test.newAccountTrieRoot
OVM_StateTransitioner.smodify.put({ await OVM_StateTransitioner.smodify.put({
postStateRoot: test.accountTrieRoot, postStateRoot: test.accountTrieRoot,
}) })
}) })
...@@ -413,8 +413,8 @@ describe('OVM_StateTransitioner', () => { ...@@ -413,8 +413,8 @@ describe('OVM_StateTransitioner', () => {
}) })
describe('commitStorageSlot', () => { describe('commitStorageSlot', () => {
beforeEach(() => { beforeEach(async () => {
OVM_StateTransitioner.smodify.set({ await OVM_StateTransitioner.smodify.put({
phase: 1, phase: 1,
}) })
}) })
...@@ -510,7 +510,7 @@ describe('OVM_StateTransitioner', () => { ...@@ -510,7 +510,7 @@ describe('OVM_StateTransitioner', () => {
storageTrieProof = storageTest.proof storageTrieProof = storageTest.proof
OVM_StateTransitioner.smodify.put({ await OVM_StateTransitioner.smodify.put({
postStateRoot: test.accountTrieRoot, postStateRoot: test.accountTrieRoot,
}) })
}) })
...@@ -529,8 +529,8 @@ describe('OVM_StateTransitioner', () => { ...@@ -529,8 +529,8 @@ describe('OVM_StateTransitioner', () => {
}) })
describe('completeTransition', () => { describe('completeTransition', () => {
beforeEach(() => { beforeEach(async () => {
OVM_StateTransitioner.smodify.set({ await OVM_StateTransitioner.smodify.put({
phase: 1, phase: 1,
}) })
}) })
......
...@@ -2,7 +2,8 @@ import { expect } from '../../../setup' ...@@ -2,7 +2,8 @@ import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { ContractFactory, Contract, constants } from 'ethers' import { ContractFactory, Contract, constants, Signer } from 'ethers'
import { MockContract, smockit } from '@eth-optimism/smock'
/* Internal Imports */ /* Internal Imports */
import { import {
...@@ -14,6 +15,11 @@ import { ...@@ -14,6 +15,11 @@ import {
const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0]) const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0])
describe('OVM_StateTransitionerFactory', () => { describe('OVM_StateTransitionerFactory', () => {
let signer1: Signer
before(async () => {
;[signer1] = await ethers.getSigners()
})
let AddressManager: Contract let AddressManager: Contract
before(async () => { before(async () => {
AddressManager = await makeAddressManager() AddressManager = await makeAddressManager()
...@@ -27,15 +33,26 @@ describe('OVM_StateTransitionerFactory', () => { ...@@ -27,15 +33,26 @@ describe('OVM_StateTransitionerFactory', () => {
}) })
let OVM_StateTransitionerFactory: Contract let OVM_StateTransitionerFactory: Contract
let Mock__OVM_StateManagerFactory: MockContract
beforeEach(async () => { beforeEach(async () => {
OVM_StateTransitionerFactory = await Factory__OVM_StateTransitionerFactory.deploy( OVM_StateTransitionerFactory = await Factory__OVM_StateTransitionerFactory.deploy(
AddressManager.address AddressManager.address
) )
Mock__OVM_StateManagerFactory = await smockit('OVM_StateManagerFactory')
Mock__OVM_StateManagerFactory.smocked.create.will.return.with(
ethers.constants.AddressZero
)
await AddressManager.setAddress(
'OVM_StateManagerFactory',
Mock__OVM_StateManagerFactory.address
)
}) })
describe('create', () => { describe('create', () => {
describe('when the sender is not the OVM_FraudVerifier', () => { describe('when the sender is not the OVM_FraudVerifier', () => {
before(async () => { beforeEach(async () => {
await AddressManager.setAddress( await AddressManager.setAddress(
'OVM_FraudVerifier', 'OVM_FraudVerifier',
constants.AddressZero constants.AddressZero
...@@ -55,5 +72,25 @@ describe('OVM_StateTransitionerFactory', () => { ...@@ -55,5 +72,25 @@ describe('OVM_StateTransitionerFactory', () => {
) )
}) })
}) })
describe('when the sender is the OVM_FraudVerifier', () => {
beforeEach(async () => {
await AddressManager.setAddress(
'OVM_FraudVerifier',
await signer1.getAddress()
)
})
it('should not revert', async () => {
await expect(
OVM_StateTransitionerFactory.connect(signer1).create(
AddressManager.address,
ethers.constants.HashZero,
ethers.constants.HashZero,
DUMMY_HASH
)
).to.not.be.reverted
})
})
}) })
}) })
...@@ -4,6 +4,9 @@ import { defaultAccounts } from 'ethereum-waffle' ...@@ -4,6 +4,9 @@ import { defaultAccounts } from 'ethereum-waffle'
import { fromHexString, toHexString } from '@eth-optimism/core-utils' import { fromHexString, toHexString } from '@eth-optimism/core-utils'
import xor from 'buffer-xor' import xor from 'buffer-xor'
/* Internal Imports */
import { getContractDefinition } from '../../src'
export const DEFAULT_ACCOUNTS = defaultAccounts export const DEFAULT_ACCOUNTS = defaultAccounts
export const DEFAULT_ACCOUNTS_HARDHAT = defaultAccounts.map((account) => { export const DEFAULT_ACCOUNTS_HARDHAT = defaultAccounts.map((account) => {
return { return {
...@@ -35,8 +38,17 @@ export const NUISANCE_GAS_COSTS = { ...@@ -35,8 +38,17 @@ export const NUISANCE_GAS_COSTS = {
MIN_GAS_FOR_INVALID_STATE_ACCESS: 30000, MIN_GAS_FOR_INVALID_STATE_ACCESS: 30000,
} }
// TODO: get this exported/imported somehow in a way that we can do math on it. unfortunately using require('.....artifacts/contract.json') is erroring... let len
export const Helper_TestRunner_BYTELEN = 3654 // This is hacky, but `hardhat compile` evaluates this file for some reason.
// Feels better to have something hacky then a constant we have to keep re-hardcoding.
try {
len = fromHexString(
getContractDefinition('Helper_TestRunner').deployedBytecode
).byteLength
/* tslint:disable:no-empty */
} catch {}
export const Helper_TestRunner_BYTELEN = len
export const STORAGE_XOR = export const STORAGE_XOR =
'0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef' '0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef'
......
...@@ -79,6 +79,11 @@ export class ExecutionManagerTestRunner { ...@@ -79,6 +79,11 @@ export class ExecutionManagerTestRunner {
}, },
}, },
}, },
ExecutionManager: {
transactionRecord: {
ovmGasRefund: 0,
},
},
} }
public run(test: TestDefinition) { public run(test: TestDefinition) {
...@@ -116,8 +121,8 @@ export class ExecutionManagerTestRunner { ...@@ -116,8 +121,8 @@ export class ExecutionManagerTestRunner {
replacedParameter = this.setPlaceholderStrings(parameter) replacedParameter = this.setPlaceholderStrings(parameter)
}) })
beforeEach(() => { beforeEach(async () => {
this.contracts.OVM_StateManager.smodify.set({ await this.contracts.OVM_StateManager.smodify.put({
accounts: { accounts: {
[this.contracts.Helper_TestRunner.address]: { [this.contracts.Helper_TestRunner.address]: {
nonce: 0, nonce: 0,
...@@ -128,11 +133,11 @@ export class ExecutionManagerTestRunner { ...@@ -128,11 +133,11 @@ export class ExecutionManagerTestRunner {
}) })
}) })
beforeEach(() => { beforeEach(async () => {
this.contracts.OVM_ExecutionManager.smodify.set( await this.contracts.OVM_ExecutionManager.smodify.put(
replacedTest.preState.ExecutionManager replacedTest.preState.ExecutionManager
) )
this.contracts.OVM_StateManager.smodify.set( await this.contracts.OVM_StateManager.smodify.put(
replacedTest.preState.StateManager replacedTest.preState.StateManager
) )
}) })
......
...@@ -168,9 +168,7 @@ interface ModifiableContractFactory extends ContractFactory { ...@@ -168,9 +168,7 @@ interface ModifiableContractFactory extends ContractFactory {
```typescript ```typescript
interface ModifiableContract extends Contract { interface ModifiableContract extends Contract {
smodify: { smodify: {
put: (storage: any) => void put: (storage: any) => Promise<void>
set: (storage: any) => void
reset: () => void
} }
} }
``` ```
...@@ -362,7 +360,7 @@ import { smoddit } from '@eth-optimism/smock' ...@@ -362,7 +360,7 @@ import { smoddit } from '@eth-optimism/smock'
const MyModifiableContractFactory = await smoddit('MyContract') const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await MyModifiableContractFactory.deploy(...) const MyModifiableContract = await MyModifiableContractFactory.deploy(...)
MyModifiableContract.smodify.put({ await MyModifiableContract.smodify.put({
myInternalUint256: 1234 myInternalUint256: 1234
}) })
...@@ -378,7 +376,7 @@ import { smoddit } from '@eth-optimism/smock' ...@@ -378,7 +376,7 @@ import { smoddit } from '@eth-optimism/smock'
const MyModifiableContractFactory = await smoddit('MyContract') const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await MyModifiableContractFactory.deploy(...) const MyModifiableContract = await MyModifiableContractFactory.deploy(...)
MyModifiableContract.smodify.put({ await MyModifiableContract.smodify.put({
myInternalStruct: { myInternalStruct: {
valueA: 1234, valueA: 1234,
valueB: true valueB: true
...@@ -397,7 +395,7 @@ import { smoddit } from '@eth-optimism/smock' ...@@ -397,7 +395,7 @@ import { smoddit } from '@eth-optimism/smock'
const MyModifiableContractFactory = await smoddit('MyContract') const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await MyModifiableContractFactory.deploy(...) const MyModifiableContract = await MyModifiableContractFactory.deploy(...)
MyModifiableContract.smodify.put({ await MyModifiableContract.smodify.put({
myInternalMapping: { myInternalMapping: {
1234: 5678 1234: 5678
} }
...@@ -415,7 +413,7 @@ import { smoddit } from '@eth-optimism/smock' ...@@ -415,7 +413,7 @@ import { smoddit } from '@eth-optimism/smock'
const MyModifiableContractFactory = await smoddit('MyContract') const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await MyModifiableContractFactory.deploy(...) const MyModifiableContract = await MyModifiableContractFactory.deploy(...)
MyModifiableContract.smodify.put({ await MyModifiableContract.smodify.put({
myInternalNestedMapping: { myInternalNestedMapping: {
1234: { 1234: {
4321: 5678 4321: 5678
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
"hardhat": "^2" "hardhat": "^2"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/core-utils": "0.1.10", "@eth-optimism/core-utils": "^0.1.10",
"@ethersproject/abi": "^5.0.13", "@ethersproject/abi": "^5.0.13",
"@ethersproject/abstract-provider": "^5.0.10", "@ethersproject/abstract-provider": "^5.0.10",
"bn.js": "^5.2.0" "bn.js": "^5.2.0"
......
...@@ -63,7 +63,7 @@ const smockifyFunction = ( ...@@ -63,7 +63,7 @@ const smockifyFunction = (
return return
}, },
get calls() { get calls() {
return vm._smockState.calls[contract.address.toLowerCase()] return (vm._smockState.calls[contract.address.toLowerCase()] || [])
.map((calldataBuf: Buffer) => { .map((calldataBuf: Buffer) => {
const sighash = toHexString(calldataBuf.slice(0, 4)) const sighash = toHexString(calldataBuf.slice(0, 4))
const fragment = contract.interface.getFunction(sighash) const fragment = contract.interface.getFunction(sighash)
......
...@@ -3,9 +3,8 @@ import hre from 'hardhat' ...@@ -3,9 +3,8 @@ import hre from 'hardhat'
import { fromHexString } from '@eth-optimism/core-utils' import { fromHexString } from '@eth-optimism/core-utils'
/* Internal Imports */ /* Internal Imports */
import { ModifiableContract, ModifiableContractFactory, Smodify } from './types' import { ModifiableContract, ModifiableContractFactory } from './types'
import { getStorageLayout, getStorageSlots } from './storage' import { getStorageLayout, getStorageSlots } from './storage'
import { bindSmod } from './binding'
import { toHexString32 } from '../utils' import { toHexString32 } from '../utils'
import { findBaseHardhatProvider } from '../common' import { findBaseHardhatProvider } from '../common'
...@@ -43,52 +42,48 @@ export const smoddit = async ( ...@@ -43,52 +42,48 @@ export const smoddit = async (
const contract: ModifiableContract = await originalDeployFn(...args) const contract: ModifiableContract = await originalDeployFn(...args)
contract._smodded = {} contract._smodded = {}
const put = (storage: any) => { const put = async (storage: any) => {
if (!storage) { if (!storage) {
return return
} }
const slots = getStorageSlots(layout, storage) const slots = getStorageSlots(layout, storage)
for (const slot of slots) { for (const slot of slots) {
contract._smodded[slot.hash.toLowerCase()] = slot.value await pStateManager.putContractStorage(
fromHexString(contract.address),
fromHexString(slot.hash.toLowerCase()),
fromHexString(slot.value)
)
} }
} }
const reset = () => {
contract._smodded = {}
}
const set = (storage: any) => {
contract.smodify.reset()
contract.smodify.put(storage)
}
const check = async (storage: any) => { const check = async (storage: any) => {
if (!storage) { if (!storage) {
return true return true
} }
const slots = getStorageSlots(layout, storage) const slots = getStorageSlots(layout, storage)
return slots.every(async (slot) => { for (const slot of slots) {
return ( if (
toHexString32( toHexString32(
await pStateManager.getContractStorage( await pStateManager.getContractStorage(
fromHexString(contract.address), fromHexString(contract.address),
fromHexString(slot.hash.toLowerCase()) fromHexString(slot.hash.toLowerCase())
) )
) === slot.value ) !== slot.value
) ) {
}) return false
}
}
return true
} }
contract.smodify = { contract.smodify = {
put, put,
reset,
set,
check, check,
} }
bindSmod(contract, provider)
return contract return contract
} }
......
...@@ -2,10 +2,8 @@ ...@@ -2,10 +2,8 @@
import { Contract, ContractFactory } from 'ethers' import { Contract, ContractFactory } from 'ethers'
export interface Smodify { export interface Smodify {
put: (storage: any) => void put: (storage: any) => Promise<void>
set: (storage: any) => void
check: (storage: any) => Promise<boolean> check: (storage: any) => Promise<boolean>
reset: () => void
} }
export interface Smodded { export interface Smodded {
......
...@@ -6,10 +6,23 @@ export const toHexString32 = ( ...@@ -6,10 +6,23 @@ export const toHexString32 = (
value: string | number | BigNumber | boolean value: string | number | BigNumber | boolean
): string => { ): string => {
if (typeof value === 'string' && value.startsWith('0x')) { if (typeof value === 'string' && value.startsWith('0x')) {
return '0x' + remove0x(value).padStart(64, '0').toLowerCase() // Known bug here is that bytes20 and address are indistinguishable but have to be treated
// differently. Address gets padded on the right, bytes20 gets padded on the left. Address is
// way more common so I'm going with the strategy of treating all bytes20 like addresses.
// Sorry to anyone who wants to smodify bytes20 values :-/ requires a bit of rewrite to fix.
if (value.length === 42) {
return '0x' + remove0x(value).padStart(64, '0').toLowerCase()
} else {
return '0x' + remove0x(value).padEnd(64, '0').toLowerCase()
}
} else if (typeof value === 'boolean') { } else if (typeof value === 'boolean') {
return toHexString32(value ? 1 : 0) return '0x' + `${value ? 1 : 0}`.padStart(64, '0')
} else { } else {
return toHexString32(BigNumber.from(value).toHexString()) return (
'0x' +
remove0x(BigNumber.from(value).toHexString())
.padStart(64, '0')
.toLowerCase()
)
} }
} }
...@@ -8,12 +8,20 @@ contract SimpleStorageGetter { ...@@ -8,12 +8,20 @@ contract SimpleStorageGetter {
bool valueB; bool valueB;
} }
address internal _address;
uint256 internal _constructorUint256; uint256 internal _constructorUint256;
uint256 internal _uint256; uint256 internal _uint256;
bool internal _bool; bool internal _bool;
SimpleStruct internal _SimpleStruct; SimpleStruct internal _SimpleStruct;
mapping (uint256 => uint256) _uint256Map; mapping (uint256 => uint256) _uint256Map;
mapping (uint256 => mapping (uint256 => uint256)) _uint256NestedMap; mapping (uint256 => mapping (uint256 => uint256)) _uint256NestedMap;
mapping (bytes5 => bool) _bytes5ToBoolMap;
mapping (address => bool) _addressToBoolMap;
mapping (address => address) _addressToAddressMap;
// Testing storage slot packing.
bool internal _packedA;
address internal _packedB;
constructor( constructor(
uint256 _inA uint256 _inA
...@@ -59,6 +67,16 @@ contract SimpleStorageGetter { ...@@ -59,6 +67,16 @@ contract SimpleStorageGetter {
return _bool; return _bool;
} }
function getAddress()
public
view
returns (
address _out
)
{
return _address;
}
function getSimpleStruct() function getSimpleStruct()
public public
view view
...@@ -93,4 +111,50 @@ contract SimpleStorageGetter { ...@@ -93,4 +111,50 @@ contract SimpleStorageGetter {
{ {
return _uint256NestedMap[_keyA][_keyB]; return _uint256NestedMap[_keyA][_keyB];
} }
function getBytes5ToBoolMapValue(
bytes5 _key
)
public
view
returns (
bool _out
)
{
return _bytes5ToBoolMap[_key];
}
function getAddressToBoolMapValue(
address _key
)
public
view
returns (
bool _out
)
{
return _addressToBoolMap[_key];
}
function getAddressToAddressMapValue(
address _key
)
public
view
returns (
address _out
)
{
return _addressToAddressMap[_key];
}
function getPackedAddress()
public
view
returns (
address
)
{
return _packedB;
}
} }
...@@ -26,7 +26,7 @@ describe('smoddit', () => { ...@@ -26,7 +26,7 @@ describe('smoddit', () => {
it('should be able to return a uint256', async () => { it('should be able to return a uint256', async () => {
const ret = 1234 const ret = 1234
smod.smodify.put({ await smod.smodify.put({
_uint256: ret, _uint256: ret,
}) })
...@@ -36,20 +36,41 @@ describe('smoddit', () => { ...@@ -36,20 +36,41 @@ describe('smoddit', () => {
it('should be able to return a boolean', async () => { it('should be able to return a boolean', async () => {
const ret = true const ret = true
smod.smodify.put({ await smod.smodify.put({
_bool: ret, _bool: ret,
}) })
expect(await smod.getBool()).to.equal(ret) expect(await smod.getBool()).to.equal(ret)
}) })
it('should be able to return an address', async () => {
const ret = '0x558ba9b8d78713fbf768c1f8a584485B4003f43F'
await smod.smodify.put({
_address: ret,
})
expect(await smod.getAddress()).to.equal(ret)
})
// TODO: Need to solve this with a rewrite.
it.skip('should be able to return an address in a packed storage slot', async () => {
const ret = '0x558ba9b8d78713fbf768c1f8a584485B4003f43F'
await smod.smodify.put({
_packedB: ret,
})
expect(await smod.getPackedAddress()).to.equal(ret)
})
it('should be able to return a simple struct', async () => { it('should be able to return a simple struct', async () => {
const ret = { const ret = {
valueA: BigNumber.from(1234), valueA: BigNumber.from(1234),
valueB: true, valueB: true,
} }
smod.smodify.put({ await smod.smodify.put({
_SimpleStruct: ret, _SimpleStruct: ret,
}) })
...@@ -62,7 +83,7 @@ describe('smoddit', () => { ...@@ -62,7 +83,7 @@ describe('smoddit', () => {
const retKey = 1234 const retKey = 1234
const retVal = 5678 const retVal = 5678
smod.smodify.put({ await smod.smodify.put({
_uint256Map: { _uint256Map: {
[retKey]: retVal, [retKey]: retVal,
}, },
...@@ -76,7 +97,7 @@ describe('smoddit', () => { ...@@ -76,7 +97,7 @@ describe('smoddit', () => {
const retKeyB = 4321 const retKeyB = 4321
const retVal = 5678 const retVal = 5678
smod.smodify.put({ await smod.smodify.put({
_uint256NestedMap: { _uint256NestedMap: {
[retKeyA]: { [retKeyA]: {
[retKeyB]: retVal, [retKeyB]: retVal,
...@@ -92,7 +113,7 @@ describe('smoddit', () => { ...@@ -92,7 +113,7 @@ describe('smoddit', () => {
it('should not return the set value if the value has been changed by the contract', async () => { it('should not return the set value if the value has been changed by the contract', async () => {
const ret = 1234 const ret = 1234
smod.smodify.put({ await smod.smodify.put({
_uint256: ret, _uint256: ret,
}) })
...@@ -104,12 +125,51 @@ describe('smoddit', () => { ...@@ -104,12 +125,51 @@ describe('smoddit', () => {
it('should return the set value if it was set in the constructor', async () => { it('should return the set value if it was set in the constructor', async () => {
const ret = 1234 const ret = 1234
smod.smodify.put({ await smod.smodify.put({
_constructorUint256: ret, _constructorUint256: ret,
}) })
expect(await smod.getConstructorUint256()).to.equal(1234) expect(await smod.getConstructorUint256()).to.equal(1234)
}) })
it('should be able to set values in a bytes5 => bool mapping', async () => {
const key = '0x0000005678'
const val = true
await smod.smodify.put({
_bytes5ToBoolMap: {
[key]: val,
},
})
expect(await smod.getBytes5ToBoolMapValue(key)).to.equal(val)
})
it('should be able to set values in a address => bool mapping', async () => {
const key = '0x558ba9b8d78713fbf768c1f8a584485B4003f43F'
const val = true
await smod.smodify.put({
_addressToBoolMap: {
[key]: val,
},
})
expect(await smod.getAddressToBoolMapValue(key)).to.equal(val)
})
it('should be able to set values in a address => address mapping', async () => {
const key = '0x558ba9b8d78713fbf768c1f8a584485B4003f43F'
const val = '0x063bE0Af9711a170BE4b07028b320C90705fec7C'
await smod.smodify.put({
_addressToAddressMap: {
[key]: val,
},
})
expect(await smod.getAddressToAddressMapValue(key)).to.equal(val)
})
}) })
}) })
}) })
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