Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
5c97a885
Commit
5c97a885
authored
Sep 16, 2020
by
Kelvin Fichter
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added test parsing
parent
954e87cd
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
867 additions
and
5 deletions
+867
-5
buidler.config.ts
packages/contracts/buidler.config.ts
+2
-0
OVM_ExecutionManager.sol
...ptimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
+15
-3
Helper_CodeContractForCalls.sol
...ts/contracts/test-helpers/Helper_CodeContractForCalls.sol
+28
-0
Helper_ModifiableStorage.sol
...racts/contracts/test-helpers/Helper_ModifiableStorage.sol
+55
-0
package.json
packages/contracts/package.json
+2
-1
OVM_test.spec.ts
...s/contracts/test/contracts/OVM/execution/OVM_test.spec.ts
+125
-0
modify-compiler.ts
packages/contracts/test/helpers/buidler/modify-compiler.ts
+75
-0
byte-utils.ts
packages/contracts/test/helpers/byte-utils.ts
+8
-0
contract-storage.ts
packages/contracts/test/helpers/storage/contract-storage.ts
+266
-0
call-generator.ts
...ges/contracts/test/helpers/test-parsing/call-generator.ts
+114
-0
parse-tests.ts
packages/contracts/test/helpers/test-parsing/parse-tests.ts
+147
-0
yarn.lock
packages/contracts/yarn.lock
+30
-1
No files found.
packages/contracts/buidler.config.ts
View file @
5c97a885
...
...
@@ -8,6 +8,8 @@ import {
usePlugin
(
'
@nomiclabs/buidler-ethers
'
)
usePlugin
(
'
@nomiclabs/buidler-waffle
'
)
import
'
./test/helpers/buidler/modify-compiler
'
const
config
:
BuidlerConfig
=
{
networks
:
{
buidlerevm
:
{
...
...
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
View file @
5c97a885
...
...
@@ -693,12 +693,16 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// existence.
(bool success, bytes memory returndata) = _target.call{gas: _gasLimit}(_data);
// Assuming there were no reverts, the message record should be accurate here. We'll update
// this value in the case of a revert.
uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft;
// Reverts at this point are completely OK, but we need to make a few updates based on the
// information passed through the revert.
if (success == false) {
(
RevertFlag flag,
uint256 nuisanceGasLeft,
uint256 nuisanceGasLeft
PostRevert
,
uint256 ovmGasRefund,
) = _decodeRevertData(returndata);
...
...
@@ -718,15 +722,18 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) {
transactionRecord.ovmGasRefund = ovmGasRefund;
}
// Reverts mean we need to use up whatever "nuisance gas" was used by the call.
// EXCEEDS_NUISANCE_GAS explicitly reduces the remaining nuisance gas for this message
// to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they
// run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags
// will simply pass up the remaining nuisance gas.
messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft)
;
nuisanceGasLeft = nuisanceGasLeftPostRevert
;
}
// We need to reset the nuisance gas back to its original value minus the amount used here.
messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft);
// Switch back to the original message context now that we're out of the call.
_switchMessageContext(_nextMessageContext, prevMessageContext);
...
...
@@ -1110,6 +1117,11 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
messageContext.ovmCALLER = _nextMessageContext.ovmCALLER;
}
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.ovmADDRESS != _nextMessageContext.ovmADDRESS) {
messageContext.ovmADDRESS = _nextMessageContext.ovmADDRESS;
}
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) {
messageContext.isStatic = _nextMessageContext.isStatic;
...
...
packages/contracts/contracts/test-helpers/Helper_CodeContractForCalls.sol
0 → 100644
View file @
5c97a885
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
struct MessageSteps {
bytes[] callsToEM;
bool shouldRevert;
}
struct EMResponse {
bool success;
bytes data;
}
contract Helper_CodeContractForCalls {
function runSteps(
MessageSteps calldata _stepsToRun
) external returns(EMResponse[] memory) {
uint numSteps = _stepsToRun.callsToEM.length;
EMResponse[] memory EMResponses = new EMResponse[](numSteps);
for (uint i = 0; i < numSteps; i++) {
bytes memory dataToSend = _stepsToRun.callsToEM[i];
(bool success, bytes memory responseData) = address(msg.sender).call(dataToSend);
EMResponses[i].success = success;
EMResponses[i].data = responseData;
}
return EMResponses; // TODO: revert with this data in case of !shouldRevert
}
}
packages/contracts/contracts/test-helpers/Helper_ModifiableStorage.sol
0 → 100644
View file @
5c97a885
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
contract Helper_ModifiableStorage {
address private target;
constructor(
address _target
) {
target = _target;
}
fallback()
external
{
(bool success, bytes memory returndata) = target.delegatecall(msg.data);
if (success) {
assembly {
return(add(returndata, 0x20), mload(returndata))
}
} else {
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
}
function __setStorageSlot(
bytes32 _key,
bytes32 _value
)
public
{
assembly {
sstore(_key, _value)
}
}
function __getStorageSlot(
bytes32 _key
)
public
view
returns (
bytes32 _value
)
{
bytes32 value;
assembly {
value := sload(_key)
}
return value;
}
}
packages/contracts/package.json
View file @
5c97a885
...
...
@@ -7,7 +7,7 @@
"build"
:
"yarn run build:contracts"
,
"build:contracts"
:
"buidler compile"
,
"test"
:
"yarn run test:contracts"
,
"test:contracts"
:
"buidler test
\"
test/contracts/OVM/execution/OVM_
ExecutionManager/opcodes/OVM_ExecutionManager.opcodes.calling
.spec.ts
\"
"
"test:contracts"
:
"buidler test
\"
test/contracts/OVM/execution/OVM_
test
.spec.ts
\"
"
},
"devDependencies"
:
{
"@nomiclabs/buidler"
:
"^1.4.4"
,
...
...
@@ -20,6 +20,7 @@
"chai"
:
"^4.2.0"
,
"ethereum-waffle"
:
"3.0.0"
,
"ethers"
:
"5.0.0"
,
"fs-extra"
:
"^9.0.1"
,
"mocha"
:
"^8.1.1"
,
"ts-node"
:
"^9.0.0"
,
"typescript"
:
"^4.0.2"
...
...
packages/contracts/test/contracts/OVM/execution/OVM_test.spec.ts
0 → 100644
View file @
5c97a885
import
{
expect
}
from
'
../../../setup
'
/* External Imports */
import
bre
,
{
ethers
}
from
'
@nomiclabs/buidler
'
import
{
Contract
}
from
'
ethers
'
/* Internal Imports */
import
{
getModifiableStorageFactory
}
from
'
../../../helpers/storage/contract-storage
'
import
{
runExecutionManagerTest
}
from
'
../../../helpers/test-parsing/parse-tests
'
import
{
NON_NULL_BYTES32
,
GAS_LIMIT
}
from
'
../../../helpers
'
const
getCodeSize
=
async
(
address
:
string
):
Promise
<
number
>
=>
{
const
code
=
await
ethers
.
provider
.
getCode
(
address
)
return
(
code
.
length
-
2
)
/
2
}
describe
(
'
OVM_StateManager
'
,
()
=>
{
let
OVM_ExecutionManager
:
Contract
let
OVM_StateManager
:
Contract
let
Helper_CodeContractForCalls
:
Contract
before
(
async
()
=>
{
const
Factory__OVM_ExecutionManager
=
await
getModifiableStorageFactory
(
'
OVM_ExecutionManager
'
)
const
Factory__OVM_StateManager
=
await
getModifiableStorageFactory
(
'
OVM_StateManager
'
)
const
Factory__Helper_CodeContractForCalls
=
await
getModifiableStorageFactory
(
'
Helper_CodeContractForCalls
'
)
OVM_ExecutionManager
=
await
Factory__OVM_ExecutionManager
.
deploy
(
'
0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c
'
)
OVM_StateManager
=
await
Factory__OVM_StateManager
.
deploy
()
Helper_CodeContractForCalls
=
await
Factory__Helper_CodeContractForCalls
.
deploy
()
})
const
DUMMY_OVM_ADDRESS_1
=
'
0x
'
+
'
12
'
.
repeat
(
20
)
const
DUMMY_OVM_ADDRESS_2
=
'
0x
'
+
'
21
'
.
repeat
(
20
)
describe
(
'
the test suite
'
,
()
=>
{
it
(
'
does the test suite
'
,
async
()
=>
{
runExecutionManagerTest
(
{
name
:
'
Top level test
'
,
preState
:
{
ExecutionManager
:
{
ovmStateManager
:
OVM_StateManager
.
address
,
messageRecord
:
{
nuisanceGasLeft
:
GAS_LIMIT
/
2
}
},
StateManager
:
{
accounts
:
{
[
DUMMY_OVM_ADDRESS_1
]:
{
codeHash
:
NON_NULL_BYTES32
,
ethAddress
:
Helper_CodeContractForCalls
.
address
},
[
DUMMY_OVM_ADDRESS_2
]:
{
codeHash
:
NON_NULL_BYTES32
,
ethAddress
:
Helper_CodeContractForCalls
.
address
}
}
}
},
postState
:
{
ExecutionManager
:
{
messageRecord
:
{
nuisanceGasLeft
:
GAS_LIMIT
/
2
-
(
await
getCodeSize
(
Helper_CodeContractForCalls
.
address
))
*
100
*
2
}
}
},
parameters
:
[
{
name
:
'
Do an ovmCALL
'
,
parameters
:
[
{
steps
:
[
{
functionName
:
'
ovmCALL
'
,
functionParams
:
[
GAS_LIMIT
/
2
,
DUMMY_OVM_ADDRESS_1
,
[
{
functionName
:
'
ovmCALL
'
,
functionParams
:
[
GAS_LIMIT
/
2
,
DUMMY_OVM_ADDRESS_2
,
[
{
functionName
:
'
ovmADDRESS
'
,
functionParams
:
[],
returnStatus
:
true
,
returnValues
:
[
DUMMY_OVM_ADDRESS_2
]
},
{
functionName
:
'
ovmCALLER
'
,
functionParams
:
[],
returnStatus
:
true
,
returnValues
:
[
DUMMY_OVM_ADDRESS_1
]
}
]
],
returnStatus
:
true
,
returnValues
:
[]
},
]
],
returnStatus
:
true
,
returnValues
:
[]
}
]
}
]
}
]
},
OVM_ExecutionManager
,
OVM_StateManager
)
})
})
})
packages/contracts/test/helpers/buidler/modify-compiler.ts
0 → 100644
View file @
5c97a885
/* External Imports */
import
fsExtra
from
'
fs-extra
'
import
{
internalTask
}
from
'
@nomiclabs/buidler/config
'
import
{
pluralize
}
from
'
@nomiclabs/buidler/internal/util/strings
'
import
{
getArtifactFromContractOutput
,
saveArtifact
}
from
'
@nomiclabs/buidler/internal/artifacts
'
import
{
TASK_COMPILE_GET_COMPILER_INPUT
,
TASK_BUILD_ARTIFACTS
,
TASK_COMPILE_GET_SOURCE_PATHS
,
TASK_COMPILE_CHECK_CACHE
,
TASK_COMPILE_COMPILE
}
from
'
@nomiclabs/buidler/builtin-tasks/task-names
'
internalTask
(
TASK_COMPILE_GET_COMPILER_INPUT
,
async
(
_
,
{
config
,
run
},
runSuper
)
=>
{
const
input
=
await
runSuper
();
// Insert the "storageLayout" input option.
input
.
settings
.
outputSelection
[
'
*
'
][
'
*
'
].
push
(
'
storageLayout
'
)
return
input
}
)
internalTask
(
TASK_BUILD_ARTIFACTS
).
setAction
(
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
file
of
Object
.
values
<
any
>
(
compilationOutput
.
contracts
))
{
for
(
const
[
contractName
,
contractOutput
]
of
Object
.
entries
(
file
))
{
const
artifact
:
any
=
getArtifactFromContractOutput
(
contractName
,
contractOutput
);
numberOfContracts
+=
1
;
// Only difference here, set the "storageLayout" field of the artifact.
artifact
.
storageLayout
=
(
contractOutput
as
any
).
storageLayout
await
saveArtifact
(
config
.
paths
.
artifacts
,
artifact
);
}
}
console
.
log
(
"
Compiled
"
,
numberOfContracts
,
pluralize
(
numberOfContracts
,
"
contract
"
),
"
successfully
"
);
}
)
packages/contracts/test/helpers/byte-utils.ts
View file @
5c97a885
...
...
@@ -5,3 +5,11 @@ export const makeHexString = (byte: string, len: number): string => {
export
const
makeAddress
=
(
byte
:
string
):
string
=>
{
return
makeHexString
(
byte
,
20
)
}
export
const
remove0x
=
(
str
:
string
):
string
=>
{
if
(
str
.
startsWith
(
'
0x
'
))
{
return
str
.
slice
(
2
)
}
else
{
return
str
}
}
packages/contracts/test/helpers/storage/contract-storage.ts
0 → 100644
View file @
5c97a885
import
bre
,
{
ethers
}
from
'
@nomiclabs/buidler
'
import
{
Contract
,
BigNumber
,
ContractFactory
}
from
"
ethers
"
;
import
{
keccak256
,
defaultAbiCoder
}
from
"
ethers/lib/utils
"
;
import
{
remove0x
}
from
'
../byte-utils
'
import
{
readArtifact
}
from
'
@nomiclabs/buidler/internal/artifacts
'
;
const
getFlattenedKeys
=
(
depth
:
number
,
value
:
any
,
):
string
[]
=>
{
if
(
depth
===
0
)
{
return
[]
}
let
keys
=
Object
.
keys
(
value
)
if
(
depth
>
1
)
{
keys
=
keys
.
concat
(
getFlattenedKeys
(
depth
-
1
,
Object
.
values
(
value
)[
0
]
)
)
}
return
keys
}
const
toHexString32
=
(
value
:
string
|
number
|
BigNumber
|
boolean
):
string
=>
{
if
(
typeof
value
===
'
string
'
)
{
return
'
0x
'
+
remove0x
(
value
).
padStart
(
64
,
'
0
'
).
toLowerCase
()
}
else
if
(
typeof
value
===
'
boolean
'
)
{
return
toHexString32
(
value
?
1
:
0
)
}
else
{
return
toHexString32
(
BigNumber
.
from
(
value
).
toHexString
())
}
}
const
getFlattenedValues
=
(
depth
:
number
,
value
:
any
):
any
[]
=>
{
if
(
depth
>
0
)
{
return
getFlattenedValues
(
depth
-
1
,
Object
.
values
(
value
)[
0
]
)
}
if
(
typeof
value
===
'
object
'
&&
value
!==
null
)
{
return
Object
.
keys
(
value
).
map
((
key
)
=>
{
return
{
label
:
key
,
value
:
toHexString32
(
value
[
key
])
}
})
}
else
{
return
[{
label
:
"
default
"
,
value
:
toHexString32
(
value
)
}]
}
}
const
getStorageSlotHash
=
(
slot
:
number
,
depth
:
number
,
value
:
any
):
string
=>
{
let
keys
=
[]
if
(
typeof
value
===
'
object
'
&&
value
!==
null
)
{
keys
=
getFlattenedKeys
(
depth
,
value
)
}
if
(
keys
.
length
===
0
)
{
return
defaultAbiCoder
.
encode
(
[
'
uint256
'
],
[
slot
]
)
}
else
{
let
slotHash
=
toHexString32
(
slot
)
for
(
const
key
of
keys
)
{
slotHash
=
keccak256
(
toHexString32
(
key
)
+
remove0x
(
slotHash
)
)
}
return
slotHash
}
}
const
parseInputSlots
=
(
layout
:
any
,
inputTypeName
:
string
):
any
[]
=>
{
const
inputType
=
layout
.
types
[
inputTypeName
]
if
(
inputType
.
encoding
===
'
mapping
'
)
{
return
parseInputSlots
(
layout
,
inputType
.
value
)
}
else
if
(
inputType
.
encoding
===
'
inplace
'
)
{
if
(
inputType
.
members
)
{
return
inputType
.
members
.
map
((
member
:
any
)
=>
{
return
{
label
:
member
.
label
,
slot
:
member
.
slot
}
})
}
else
{
return
[{
label
:
"
default
"
,
slot
:
0
}]
}
}
else
{
throw
new
Error
(
'
Encoding type not supported.
'
)
}
}
export
const
getModifiableStorageFactory
=
async
(
name
:
string
):
Promise
<
ContractFactory
>
=>
{
const
contractFactory
=
await
ethers
.
getContractFactory
(
name
)
const
proxyFactory
=
await
ethers
.
getContractFactory
(
'
Helper_ModifiableStorage
'
)
const
originalDeploy
=
contractFactory
.
deploy
.
bind
(
contractFactory
)
contractFactory
.
deploy
=
async
(...
args
:
any
[]):
Promise
<
Contract
>
=>
{
const
originalDefinePropertyFn
=
Object
.
defineProperty
Object
.
defineProperty
=
(
object
:
any
,
name
:
string
,
props
:
any
):
void
=>
{
if
(
props
.
writable
===
false
)
{
props
.
writable
=
true
}
originalDefinePropertyFn
(
object
,
name
,
props
)
}
const
contract
=
await
originalDeploy
(...
args
)
const
proxy
=
await
proxyFactory
.
deploy
(
contract
.
address
)
;(
contract
as
any
).
address
=
proxy
.
address
;(
contract
as
any
).
resolvedAddress
=
proxy
.
address
;(
contract
as
any
).
__setStorageSlot
=
proxy
.
__setStorageSlot
.
bind
(
proxy
)
;(
contract
as
any
).
__getStorageSlot
=
proxy
.
__getStorageSlot
.
bind
(
proxy
)
;(
contract
as
any
).
__setContractStorage
=
async
(
value
:
any
)
=>
{
await
setContractStorage
(
contract
,
(
await
readArtifact
(
bre
.
config
.
paths
.
artifacts
,
name
)
as
any
).
storageLayout
,
value
)
}
;(
contract
as
any
).
__checkContractStorage
=
async
(
value
:
any
)
=>
{
await
checkContractStorage
(
contract
,
(
await
readArtifact
(
bre
.
config
.
paths
.
artifacts
,
name
)
as
any
).
storageLayout
,
value
)
}
Object
.
defineProperty
=
originalDefinePropertyFn
return
contract
}
return
contractFactory
}
export
const
setContractStorage
=
async
(
contract
:
Contract
,
layout
:
any
,
storage
:
any
):
Promise
<
void
>
=>
{
storage
=
storage
||
{}
for
(
const
[
key
,
value
]
of
Object
.
entries
(
storage
))
{
const
layoutMap
=
layout
.
storage
.
find
((
layoutMap
:
any
)
=>
{
return
layoutMap
.
label
===
key
})
const
inputSlots
=
parseInputSlots
(
layout
,
layoutMap
.
type
)
const
slot
=
parseInt
(
layoutMap
.
slot
)
const
depth
=
(
layoutMap
.
type
.
match
(
/t_mapping/g
)
||
[]).
length
if
(
typeof
value
!==
'
object
'
)
{
const
slotHash
=
getStorageSlotHash
(
slot
,
depth
,
value
)
await
contract
.
__setStorageSlot
(
slotHash
,
toHexString32
(
value
as
string
)
)
}
else
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
[
subKey
]:
subValue
})
const
slotValues
=
getFlattenedValues
(
depth
,
{
[
subKey
]:
subValue
})
for
(
let
i
=
0
;
i
<
slotValues
.
length
;
i
++
)
{
const
slotValue
=
slotValues
[
i
]
const
slotIndex
=
inputSlots
.
find
((
inputSlot
)
=>
{
return
inputSlot
.
label
===
slotValue
.
label
}).
slot
const
slotHash
=
toHexString32
(
BigNumber
.
from
(
baseSlotHash
).
add
(
slotIndex
))
await
contract
.
__setStorageSlot
(
slotHash
,
slotValue
.
value
)
}
}
}
}
}
export
const
checkContractStorage
=
async
(
contract
:
Contract
,
layout
:
any
,
storage
:
any
):
Promise
<
void
>
=>
{
storage
=
storage
||
{}
for
(
const
[
key
,
value
]
of
Object
.
entries
(
storage
))
{
const
layoutMap
=
layout
.
storage
.
find
((
layoutMap
:
any
)
=>
{
return
layoutMap
.
label
===
key
})
const
inputSlots
=
parseInputSlots
(
layout
,
layoutMap
.
type
)
const
slot
=
parseInt
(
layoutMap
.
slot
)
const
depth
=
(
layoutMap
.
type
.
match
(
/t_mapping/g
)
||
[]).
length
if
(
typeof
value
!==
'
object
'
)
{
const
slotHash
=
getStorageSlotHash
(
slot
,
depth
,
value
)
const
retSlotValue
=
await
contract
.
__getStorageSlot
(
slotHash
)
if
(
retSlotValue
!==
toHexString32
(
value
as
string
))
{
throw
new
Error
(
`Resulting state of
${
key
}
(
${
retSlotValue
}
) did not match expected state (
${
toHexString32
(
value
as
string
)}
)`
)
}
}
else
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
[
subKey
]:
subValue
})
const
slotValues
=
getFlattenedValues
(
depth
,
{
[
subKey
]:
subValue
})
for
(
let
i
=
0
;
i
<
slotValues
.
length
;
i
++
)
{
const
slotValue
=
slotValues
[
i
]
const
slotIndex
=
inputSlots
.
find
((
inputSlot
)
=>
{
return
inputSlot
.
label
===
slotValue
.
label
}).
slot
const
slotHash
=
toHexString32
(
BigNumber
.
from
(
baseSlotHash
).
add
(
slotIndex
))
const
retSlotValue
=
await
contract
.
__getStorageSlot
(
slotHash
)
if
(
retSlotValue
!==
slotValue
.
value
)
{
throw
new
Error
(
`Resulting state of
${
slotValue
.
label
}
(
${
retSlotValue
}
) did not match expected state (
${
slotValue
.
value
}
).`
)
}
}
}
}
}
}
packages/contracts/test/helpers/test-parsing/call-generator.ts
0 → 100644
View file @
5c97a885
import
*
as
path
from
'
path
'
import
{
ethers
}
from
"
@nomiclabs/buidler
"
import
{
ContractFactory
}
from
'
ethers
'
import
{
Interface
}
from
'
ethers/lib/utils
'
const
getContractDefinition
=
(
name
:
string
):
any
=>
{
return
require
(
path
.
join
(
__dirname
,
'
../../../artifacts
'
,
`
${
name
}
.json`
))
}
export
const
getContractInterface
=
(
name
:
string
):
Interface
=>
{
const
definition
=
getContractDefinition
(
name
)
return
new
ethers
.
utils
.
Interface
(
definition
.
abi
)
}
// const ExecutionManager: ContractFactory = await ethers.getContractFactory('iOVM_ExecutionManager')
export
const
iEM
=
getContractInterface
(
'
iOVM_ExecutionManager
'
)
// const CodeContract: ContractFactory = await ethers.getContractFactory('Helper_CodeContractForCalls')
const
iCC
=
getContractInterface
(
'
Helper_CodeContractForCalls
'
)
export
interface
TestCallGenerator
{
generateCalldata
():
string
generateExpectedReturnData
():
string
parseActualReturnData
(
returned
:
string
):
any
}
export
abstract
class
baseOVMCallTest
implements
TestCallGenerator
{
executionManagerMethodName
:
string
=
'
EM METHOD NAME NOT SET
'
arguments
:
any
[]
=
[]
expectedReturnValues
:
any
[]
=
[]
generateCalldata
():
string
{
return
iEM
.
encodeFunctionData
(
this
.
executionManagerMethodName
,
this
.
arguments
)
}
generateExpectedReturnData
():
string
{
return
iEM
.
encodeFunctionResult
(
this
.
executionManagerMethodName
,
this
.
expectedReturnValues
)
}
parseActualReturnData
(
returned
:
string
)
{
const
decodedResult
=
iEM
.
decodeFunctionResult
(
this
.
executionManagerMethodName
,
returned
)
return
'
call to ExeMgr.
'
+
this
.
executionManagerMethodName
+
'
returned:
\n
'
+
decodedResult
}
}
export
class
ovmADDRESSTest
extends
baseOVMCallTest
{
constructor
(
expectedAddress
:
string
)
{
super
()
this
.
executionManagerMethodName
=
'
ovmADDRESS
'
this
.
expectedReturnValues
=
[
expectedAddress
]
}
}
export
class
ovmCALLERTest
extends
baseOVMCallTest
{
constructor
(
expectedMsgSender
:
string
)
{
super
()
this
.
executionManagerMethodName
=
'
ovmCALLER
'
this
.
expectedReturnValues
=
[
expectedMsgSender
]
}
}
const
DEFAULT_GAS_LIMIT
=
1
_000_000
export
class
ovmCALLTest
extends
baseOVMCallTest
{
constructor
(
callee
:
string
,
calleeTests
:
Array
<
TestCallGenerator
>
,
shouldCalleeSucceed
:
boolean
=
true
,
gasLimit
:
number
=
DEFAULT_GAS_LIMIT
)
{
super
()
this
.
executionManagerMethodName
=
'
ovmCALL
'
this
.
arguments
=
[
gasLimit
,
callee
,
iCC
.
encodeFunctionData
(
'
runSteps
'
,
[{
callsToEM
:
calleeTests
.
map
((
testGenerator
)
=>
{
return
testGenerator
.
generateCalldata
()
}),
shouldRevert
:
!
shouldCalleeSucceed
}]
)
]
this
.
expectedReturnValues
=
[
shouldCalleeSucceed
,
iCC
.
encodeFunctionResult
(
'
runSteps
'
,
[
calleeTests
.
map
((
testGenerator
)
=>
{
return
{
success
:
true
,
//TODO: figure out if we need this not to happen
data
:
testGenerator
.
generateExpectedReturnData
()
}
})]
)
]
}
}
packages/contracts/test/helpers/test-parsing/parse-tests.ts
0 → 100644
View file @
5c97a885
import
{
expect
}
from
'
../../setup
'
import
{
Contract
,
BigNumber
}
from
"
ethers
"
import
{
TestCallGenerator
,
ovmADDRESSTest
,
ovmCALLERTest
,
ovmCALLTest
}
from
"
./call-generator
"
import
{
GAS_LIMIT
}
from
'
../constants
'
type
SolidityFunctionParameter
=
string
|
number
|
BigNumber
interface
TestStep
{
functionName
:
string
functionParams
:
Array
<
SolidityFunctionParameter
|
TestStep
[]
>
returnStatus
:
boolean
returnValues
:
any
[]
}
interface
TestParameters
{
steps
:
TestStep
[]
}
interface
TestDefinition
{
name
:
string
preState
?:
{
ExecutionManager
?:
any
,
StateManager
?:
any
},
parameters
:
Array
<
TestParameters
|
TestDefinition
>
,
postState
?:
{
ExecutionManager
?:
any
,
StateManager
?:
any
},
}
const
isTestDefinition
=
(
parameters
:
TestParameters
|
TestDefinition
):
parameters
is
TestDefinition
=>
{
return
(
parameters
as
TestDefinition
).
name
!==
undefined
}
/*
const encodeTestStep = (
step: TestStep,
ovmExecutionManager: Contract,
helperCodeContract: Contract
): string => {
const params = step.functionParams.map((functionParam) => {
if (Array.isArray(functionParam)) {
return
}
})
return ovmExecutionManager.interface.encodeFunctionData(
step.functionName,
params
)
}
*/
const
getCorrectGenerator
=
(
step
:
TestStep
):
TestCallGenerator
=>
{
switch
(
step
.
functionName
)
{
case
'
ovmADDRESS
'
:
return
new
ovmADDRESSTest
(
step
.
returnValues
[
0
])
case
'
ovmCALLER
'
:
return
new
ovmCALLERTest
(
step
.
returnValues
[
0
])
case
'
ovmCALL
'
:
return
new
ovmCALLTest
(
step
.
functionParams
[
1
]
as
string
,
(
step
.
functionParams
[
2
]
as
TestStep
[]).
map
((
param
)
=>
{
return
getCorrectGenerator
(
param
)
}),
step
.
returnStatus
,
step
.
functionParams
[
0
]
as
number
)
default
:
throw
new
Error
(
'
Input type not implemented.
'
)
}
}
const
getTestGenerator
=
(
step
:
TestStep
):
TestCallGenerator
=>
{
return
getCorrectGenerator
(
step
)
}
export
const
runExecutionManagerTest
=
(
test
:
TestDefinition
,
ovmExecutionManager
:
Contract
,
ovmStateManager
:
Contract
):
void
=>
{
test
.
preState
=
test
.
preState
||
{}
test
.
postState
=
test
.
postState
||
{}
describe
(
`Standard test:
${
test
.
name
}
`
,
()
=>
{
test
.
parameters
.
map
((
parameters
)
=>
{
if
(
isTestDefinition
(
parameters
))
{
runExecutionManagerTest
(
{
...
parameters
,
preState
:
{
...
test
.
preState
,
...
parameters
.
preState
},
postState
:
{
...
test
.
postState
,
...
parameters
.
postState
}
},
ovmExecutionManager
,
ovmStateManager
,
)
}
else
{
beforeEach
(
async
()
=>
{
await
ovmExecutionManager
.
__setContractStorage
(
test
.
preState
.
ExecutionManager
)
await
ovmStateManager
.
__setContractStorage
(
test
.
preState
.
StateManager
)
})
afterEach
(
async
()
=>
{
await
ovmExecutionManager
.
__checkContractStorage
({
...
test
.
preState
.
ExecutionManager
,
...
test
.
postState
.
ExecutionManager
})
await
ovmStateManager
.
__checkContractStorage
({
...
test
.
preState
.
StateManager
,
...
test
.
postState
.
StateManager
})
})
parameters
.
steps
.
map
((
step
,
idx
)
=>
{
it
(
`should run test:
${
test
.
name
}
${
idx
}
`
,
async
()
=>
{
const
testGenerator
=
getTestGenerator
(
step
)
const
callResult
=
await
ovmExecutionManager
.
provider
.
call
({
to
:
ovmExecutionManager
.
address
,
data
:
testGenerator
.
generateCalldata
(),
gasLimit
:
GAS_LIMIT
})
await
ovmExecutionManager
.
signer
.
sendTransaction
({
to
:
ovmExecutionManager
.
address
,
data
:
testGenerator
.
generateCalldata
(),
gasLimit
:
GAS_LIMIT
})
expect
(
callResult
).
to
.
equal
(
testGenerator
.
generateExpectedReturnData
())
})
})
}
})
})
}
packages/contracts/yarn.lock
View file @
5c97a885
...
...
@@ -1126,6 +1126,11 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
at-least-node@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
atob@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
...
...
@@ -4065,6 +4070,16 @@ fs-extra@^7.0.1:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==
dependencies:
at-least-node "^1.0.0"
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^1.0.0"
fs-minipass@^1.2.5:
version "1.2.7"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
...
...
@@ -4379,7 +4394,7 @@ got@^8.3.1:
url-parse-lax "^3.0.0"
url-to-options "^1.0.1"
graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9
, graceful-fs@^4.2.0
:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
...
...
@@ -5261,6 +5276,15 @@ jsonfile@^4.0.0:
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179"
integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==
dependencies:
universalify "^1.0.0"
optionalDependencies:
graceful-fs "^4.1.6"
jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
...
...
@@ -8211,6 +8235,11 @@ universalify@^0.1.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
universalify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==
unorm@^1.3.3:
version "1.6.0"
resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment