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
8d8e72dd
Commit
8d8e72dd
authored
Sep 21, 2020
by
Kelvin Fichter
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Force invalid state access check on SSTORE
parent
4a177ba1
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
304 additions
and
62 deletions
+304
-62
OVM_ExecutionManager.sol
...ptimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
+6
-0
OVM_StateManager.sol
...ts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol
+8
-1
package.json
packages/contracts/package.json
+1
-1
run.spec.ts
.../contracts/OVM/execution/OVM_ExecutionManager/run.spec.ts
+112
-0
contract-storage.ts
packages/contracts/test/helpers/storage/contract-storage.ts
+101
-39
test-runner.ts
packages/contracts/test/helpers/test-utils/test-runner.ts
+46
-11
test.types.ts
packages/contracts/test/helpers/test-utils/test.types.ts
+30
-10
No files found.
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
View file @
8d8e72dd
...
@@ -1216,6 +1216,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
...
@@ -1216,6 +1216,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
)
)
internal
internal
{
{
// We need to make sure that the transaction isn't trying to access storage that hasn't
// been provided to the OVM_StateManager. We'll immediately abort if this is the case.
_checkInvalidStateAccess(
ovmStateManager.hasContractStorage(_contract, _key)
);
// Check whether the slot has been changed before and mark it as changed if not. We need
// Check whether the slot has been changed before and mark it as changed if not. We need
// this because "nuisance gas" only applies to the first time that a slot is changed.
// this because "nuisance gas" only applies to the first time that a slot is changed.
(
(
...
...
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol
View file @
8d8e72dd
...
@@ -366,8 +366,15 @@ contract OVM_StateManager is iOVM_StateManager {
...
@@ -366,8 +366,15 @@ contract OVM_StateManager is iOVM_StateManager {
authenticated
authenticated
{
{
contractStorage[_contract][_key] = _value;
contractStorage[_contract][_key] = _value;
// Only used when initially populating the contract storage. OVM_ExecutionManager will
// perform a `hasContractStorage` INVALID_STATE_ACCESS check before putting any contract
// storage because writing to zero when the actual value is nonzero causes a gas
// discrepancy.
if (verifiedContractStorage[_contract][_key] == false) {
verifiedContractStorage[_contract][_key] = true;
verifiedContractStorage[_contract][_key] = true;
}
}
}
/**
/**
* Retrieves a contract storage slot value.
* Retrieves a contract storage slot value.
...
...
packages/contracts/package.json
View file @
8d8e72dd
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
"build"
:
"yarn run build:contracts"
,
"build"
:
"yarn run build:contracts"
,
"build:contracts"
:
"buidler compile"
,
"build:contracts"
:
"buidler compile"
,
"test"
:
"yarn run test:contracts"
,
"test"
:
"yarn run test:contracts"
,
"test:contracts"
:
"buidler test
\"
test/contracts/OVM/execution/OVM_ExecutionManager/
ovmDELEGATECALL
.spec.ts
\"
"
,
"test:contracts"
:
"buidler test
\"
test/contracts/OVM/execution/OVM_ExecutionManager/
run
.spec.ts
\"
"
,
"lint"
:
"tslint --format stylish --project ."
,
"lint"
:
"tslint --format stylish --project ."
,
"fix"
:
"prettier --config prettier-config.json --write
\"
buidler.config.ts
\"
\"
{src,test}/**/*.ts
\"
"
"fix"
:
"prettier --config prettier-config.json --write
\"
buidler.config.ts
\"
\"
{src,test}/**/*.ts
\"
"
},
},
...
...
packages/contracts/test/contracts/OVM/execution/OVM_ExecutionManager/run.spec.ts
0 → 100644
View file @
8d8e72dd
/* Internal Imports */
import
{
ExecutionManagerTestRunner
,
TestDefinition
,
GAS_LIMIT
,
NON_NULL_BYTES32
,
REVERT_FLAGS
,
ZERO_ADDRESS
,
VERIFIED_EMPTY_CONTRACT_HASH
,
}
from
'
../../../../helpers
'
const
DUMMY_REVERT_DATA
=
'
0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420
'
const
GAS_METADATA_ADDRESS
=
'
0x06a506a506a506a506a506a506a506a506a506a5
'
enum
GasMetadataKey
{
CURRENT_EPOCH_START_TIMESTAMP
,
CUMULATIVE_SEQUENCER_QUEUE_GAS
,
CUMULATIVE_L1TOL2_QUEUE_GAS
,
PREV_EPOCH_SEQUENCER_QUEUE_GAS
,
PREV_EPOCH_L1TOL2_QUEUE_GAS
}
const
keyToBytes32
=
(
key
:
GasMetadataKey
):
string
=>
{
return
'
0x
'
+
`0
${
key
}
`
.
padStart
(
64
,
'
0
'
)
}
const
test_run
:
TestDefinition
=
{
name
:
'
Basic tests for ovmCALL
'
,
preState
:
{
ExecutionManager
:
{
ovmStateManager
:
'
$OVM_STATE_MANAGER
'
,
ovmSafetyChecker
:
'
$OVM_SAFETY_CHECKER
'
,
messageRecord
:
{
nuisanceGasLeft
:
GAS_LIMIT
,
},
},
StateManager
:
{
owner
:
'
$OVM_EXECUTION_MANAGER
'
,
accounts
:
{
$DUMMY_OVM_ADDRESS_1
:
{
codeHash
:
NON_NULL_BYTES32
,
ethAddress
:
'
$OVM_CALL_HELPER
'
,
},
$DUMMY_OVM_ADDRESS_2
:
{
codeHash
:
NON_NULL_BYTES32
,
ethAddress
:
'
$OVM_CALL_HELPER
'
,
},
$DUMMY_OVM_ADDRESS_3
:
{
codeHash
:
VERIFIED_EMPTY_CONTRACT_HASH
,
ethAddress
:
'
0x
'
+
'
00
'
.
repeat
(
20
),
},
},
contractStorage
:
{
[
GAS_METADATA_ADDRESS
]:
{
[
keyToBytes32
(
GasMetadataKey
.
CURRENT_EPOCH_START_TIMESTAMP
)]:
1
,
[
keyToBytes32
(
GasMetadataKey
.
CUMULATIVE_SEQUENCER_QUEUE_GAS
)]:
0
,
[
keyToBytes32
(
GasMetadataKey
.
CUMULATIVE_L1TOL2_QUEUE_GAS
)]:
0
,
[
keyToBytes32
(
GasMetadataKey
.
PREV_EPOCH_SEQUENCER_QUEUE_GAS
)]:
0
,
[
keyToBytes32
(
GasMetadataKey
.
PREV_EPOCH_L1TOL2_QUEUE_GAS
)]:
0
}
},
verifiedContractStorage
:
{
[
GAS_METADATA_ADDRESS
]:
{
[
keyToBytes32
(
GasMetadataKey
.
CURRENT_EPOCH_START_TIMESTAMP
)]:
true
,
[
keyToBytes32
(
GasMetadataKey
.
CUMULATIVE_SEQUENCER_QUEUE_GAS
)]:
true
,
[
keyToBytes32
(
GasMetadataKey
.
CUMULATIVE_L1TOL2_QUEUE_GAS
)]:
true
,
[
keyToBytes32
(
GasMetadataKey
.
PREV_EPOCH_SEQUENCER_QUEUE_GAS
)]:
true
,
[
keyToBytes32
(
GasMetadataKey
.
PREV_EPOCH_L1TOL2_QUEUE_GAS
)]:
true
,
}
}
},
},
parameters
:
[
{
name
:
'
run => ovmCALL(ADDRESS_1) => ovmADDRESS
'
,
focus
:
true
,
steps
:
[
{
functionName
:
'
run
'
,
functionParams
:
{
timestamp
:
0
,
queueOrigin
:
0
,
entrypoint
:
'
$OVM_CALL_HELPER
'
,
origin
:
ZERO_ADDRESS
,
msgSender
:
ZERO_ADDRESS
,
gasLimit
:
GAS_LIMIT
,
subSteps
:
[
{
functionName
:
'
ovmCALL
'
,
functionParams
:
{
gasLimit
:
GAS_LIMIT
,
target
:
'
$DUMMY_OVM_ADDRESS_1
'
,
subSteps
:
[
{
functionName
:
'
ovmADDRESS
'
,
expectedReturnValue
:
'
$DUMMY_OVM_ADDRESS_1
'
,
},
],
},
expectedReturnStatus
:
true
,
},
]
}
}
],
},
]
}
const
runner
=
new
ExecutionManagerTestRunner
()
runner
.
run
(
test_run
)
packages/contracts/test/helpers/storage/contract-storage.ts
View file @
8d8e72dd
...
@@ -166,11 +166,38 @@ export const setContractStorage = async (
...
@@ -166,11 +166,38 @@ export const setContractStorage = async (
const
inputSlots
=
parseInputSlots
(
layout
,
layoutMap
.
type
)
const
inputSlots
=
parseInputSlots
(
layout
,
layoutMap
.
type
)
const
slot
=
parseInt
(
layoutMap
.
slot
,
10
)
const
slot
=
parseInt
(
layoutMap
.
slot
,
10
)
cons
t
depth
=
(
layoutMap
.
type
.
match
(
/t_mapping/g
)
||
[]).
length
le
t
depth
=
(
layoutMap
.
type
.
match
(
/t_mapping/g
)
||
[]).
length
if
(
typeof
value
!==
'
object
'
)
{
if
(
typeof
value
!==
'
object
'
)
{
const
slotHash
=
getStorageSlotHash
(
slot
,
depth
,
value
)
const
slotHash
=
getStorageSlotHash
(
slot
,
depth
,
value
)
await
contract
.
__setStorageSlot
(
slotHash
,
toHexString32
(
value
as
string
))
await
contract
.
__setStorageSlot
(
slotHash
,
toHexString32
(
value
as
string
))
}
else
{
if
(
key
===
'
contractStorage
'
||
key
===
'
verifiedContractStorage
'
)
{
for
(
const
[
subKey1
,
subValue1
]
of
Object
.
entries
(
value
))
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
subValue1
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
[
subKey1
]:
{
[
subKey
]:
subValue
,
}
})
const
slotValues
=
getFlattenedValues
(
depth
,
{
[
subKey1
]:
{
[
subKey
]:
subValue
,
}
})
for
(
const
slotValue
of
slotValues
)
{
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
)
}
}
}
}
else
{
}
else
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
...
@@ -193,6 +220,7 @@ export const setContractStorage = async (
...
@@ -193,6 +220,7 @@ export const setContractStorage = async (
}
}
}
}
}
}
}
}
}
export
const
checkContractStorage
=
async
(
export
const
checkContractStorage
=
async
(
...
@@ -222,6 +250,39 @@ export const checkContractStorage = async (
...
@@ -222,6 +250,39 @@ export const checkContractStorage = async (
)}
)`
)}
)`
)
)
}
}
}
else
{
if
(
key
===
'
contractStorage
'
||
key
===
'
verifiedContractStorage
'
)
{
for
(
const
[
subKey1
,
subValue1
]
of
Object
.
entries
(
value
))
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
subValue1
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
[
subKey1
]:
{
[
subKey
]:
subValue
,
}
})
const
slotValues
=
getFlattenedValues
(
depth
,
{
[
subKey1
]:
{
[
subKey
]:
subValue
,
}
})
for
(
const
slotValue
of
slotValues
)
{
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
}
).`
)
}
}
}
}
}
else
{
}
else
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
...
@@ -250,4 +311,5 @@ export const checkContractStorage = async (
...
@@ -250,4 +311,5 @@ export const checkContractStorage = async (
}
}
}
}
}
}
}
}
}
packages/contracts/test/helpers/test-utils/test-runner.ts
View file @
8d8e72dd
...
@@ -8,17 +8,20 @@ import { cloneDeep } from 'lodash'
...
@@ -8,17 +8,20 @@ import { cloneDeep } from 'lodash'
/* Internal Imports */
/* Internal Imports */
import
{
import
{
TestDefinition
,
TestDefinition
,
ParsedTestStep
,
TestParameter
,
TestStep
,
TestStep
,
TestStep_CALL
,
TestStep_Run
,
isRevertFlagError
,
isTestStep_SSTORE
,
isTestStep_SSTORE
,
isTestStep_SLOAD
,
isTestStep_SLOAD
,
isTestStep_CALL
,
isTestStep_CALL
,
isTestStep_CREATE
,
isTestStep_CREATE
,
isTestStep_CREATE2
,
isTestStep_CREATE2
,
isTestStep_Context
,
isTestStep_Context
,
ParsedTestStep
,
isRevertFlagError
,
TestParameter
,
isTestStep_evm
,
isTestStep_evm
,
isTestStep_Run
,
isTestStep_EXTCODESIZE
,
isTestStep_EXTCODESIZE
,
isTestStep_EXTCODEHASH
,
isTestStep_EXTCODEHASH
,
isTestStep_EXTCODECOPY
,
isTestStep_EXTCODECOPY
,
...
@@ -190,7 +193,38 @@ export class ExecutionManagerTestRunner {
...
@@ -190,7 +193,38 @@ export class ExecutionManagerTestRunner {
return
ret
return
ret
}
}
private
async
runTestStep
(
step
:
TestStep
)
{
private
async
runTestStep
(
step
:
TestStep
|
TestStep_Run
)
{
if
(
isTestStep_Run
(
step
))
{
let
calldata
:
string
if
(
step
.
functionParams
.
data
)
{
calldata
=
step
.
functionParams
.
data
}
else
{
const
runStep
:
TestStep_CALL
=
{
functionName
:
'
ovmCALL
'
,
functionParams
:
{
gasLimit
:
GAS_LIMIT
,
target
:
this
.
contracts
.
Helper_TestRunner
.
address
,
subSteps
:
step
.
functionParams
.
subSteps
},
expectedReturnStatus
:
true
}
calldata
=
this
.
encodeFunctionData
(
runStep
)
}
await
this
.
contracts
.
OVM_ExecutionManager
.
run
(
{
timestamp
:
step
.
functionParams
.
timestamp
,
queueOrigin
:
step
.
functionParams
.
queueOrigin
,
entrypoint
:
step
.
functionParams
.
entrypoint
,
origin
:
step
.
functionParams
.
origin
,
msgSender
:
step
.
functionParams
.
msgSender
,
gasLimit
:
step
.
functionParams
.
gasLimit
,
data
:
calldata
},
this
.
contracts
.
OVM_StateManager
.
address
)
}
else
{
await
this
.
contracts
.
OVM_ExecutionManager
.
ovmCALL
(
await
this
.
contracts
.
OVM_ExecutionManager
.
ovmCALL
(
GAS_LIMIT
/
2
,
GAS_LIMIT
/
2
,
this
.
contracts
.
Helper_TestRunner
.
address
,
this
.
contracts
.
Helper_TestRunner
.
address
,
...
@@ -200,6 +234,7 @@ export class ExecutionManagerTestRunner {
...
@@ -200,6 +234,7 @@ export class ExecutionManagerTestRunner {
)
)
)
)
}
}
}
private
parseTestStep
(
step
:
TestStep
):
ParsedTestStep
{
private
parseTestStep
(
step
:
TestStep
):
ParsedTestStep
{
return
{
return
{
...
...
packages/contracts/test/helpers/test-utils/test.types.ts
View file @
8d8e72dd
...
@@ -83,7 +83,7 @@ interface TestStep_SLOAD {
...
@@ -83,7 +83,7 @@ interface TestStep_SLOAD {
expectedReturnValue
:
string
|
RevertFlagError
expectedReturnValue
:
string
|
RevertFlagError
}
}
interface
TestStep_CALL
{
export
interface
TestStep_CALL
{
functionName
:
CallOpcode
functionName
:
CallOpcode
functionParams
:
{
functionParams
:
{
gasLimit
:
number
|
BigNumber
gasLimit
:
number
|
BigNumber
...
@@ -116,6 +116,20 @@ interface TestStep_CREATE2 {
...
@@ -116,6 +116,20 @@ interface TestStep_CREATE2 {
expectedReturnValue
:
string
|
RevertFlagError
expectedReturnValue
:
string
|
RevertFlagError
}
}
export
interface
TestStep_Run
{
functionName
:
'
run
'
,
functionParams
:
{
timestamp
:
number
queueOrigin
:
number
entrypoint
:
string
origin
:
string
msgSender
:
string
gasLimit
:
number
data
?:
string
subSteps
?:
TestStep
[]
}
}
export
type
TestStep
=
export
type
TestStep
=
|
TestStep_Context
|
TestStep_Context
|
TestStep_SSTORE
|
TestStep_SSTORE
...
@@ -164,33 +178,33 @@ export const isTestStep_Context = (
...
@@ -164,33 +178,33 @@ export const isTestStep_Context = (
}
}
export
const
isTestStep_SSTORE
=
(
step
:
TestStep
):
step
is
TestStep_SSTORE
=>
{
export
const
isTestStep_SSTORE
=
(
step
:
TestStep
):
step
is
TestStep_SSTORE
=>
{
return
step
.
functionName
==
'
ovmSSTORE
'
return
step
.
functionName
==
=
'
ovmSSTORE
'
}
}
export
const
isTestStep_SLOAD
=
(
step
:
TestStep
):
step
is
TestStep_SLOAD
=>
{
export
const
isTestStep_SLOAD
=
(
step
:
TestStep
):
step
is
TestStep_SLOAD
=>
{
return
step
.
functionName
==
'
ovmSLOAD
'
return
step
.
functionName
==
=
'
ovmSLOAD
'
}
}
export
const
isTestStep_EXTCODESIZE
=
(
export
const
isTestStep_EXTCODESIZE
=
(
step
:
TestStep
step
:
TestStep
):
step
is
TestStep_EXTCODESIZE
=>
{
):
step
is
TestStep_EXTCODESIZE
=>
{
return
step
.
functionName
==
'
ovmEXTCODESIZE
'
return
step
.
functionName
==
=
'
ovmEXTCODESIZE
'
}
}
export
const
isTestStep_EXTCODEHASH
=
(
export
const
isTestStep_EXTCODEHASH
=
(
step
:
TestStep
step
:
TestStep
):
step
is
TestStep_EXTCODEHASH
=>
{
):
step
is
TestStep_EXTCODEHASH
=>
{
return
step
.
functionName
==
'
ovmEXTCODEHASH
'
return
step
.
functionName
==
=
'
ovmEXTCODEHASH
'
}
}
export
const
isTestStep_EXTCODECOPY
=
(
export
const
isTestStep_EXTCODECOPY
=
(
step
:
TestStep
step
:
TestStep
):
step
is
TestStep_EXTCODECOPY
=>
{
):
step
is
TestStep_EXTCODECOPY
=>
{
return
step
.
functionName
==
'
ovmEXTCODECOPY
'
return
step
.
functionName
==
=
'
ovmEXTCODECOPY
'
}
}
export
const
isTestStep_REVERT
=
(
step
:
TestStep
):
step
is
TestStep_REVERT
=>
{
export
const
isTestStep_REVERT
=
(
step
:
TestStep
):
step
is
TestStep_REVERT
=>
{
return
step
.
functionName
==
'
ovmREVERT
'
return
step
.
functionName
==
=
'
ovmREVERT
'
}
}
export
const
isTestStep_CALL
=
(
step
:
TestStep
):
step
is
TestStep_CALL
=>
{
export
const
isTestStep_CALL
=
(
step
:
TestStep
):
step
is
TestStep_CALL
=>
{
...
@@ -200,13 +214,19 @@ export const isTestStep_CALL = (step: TestStep): step is TestStep_CALL => {
...
@@ -200,13 +214,19 @@ export const isTestStep_CALL = (step: TestStep): step is TestStep_CALL => {
}
}
export
const
isTestStep_CREATE
=
(
step
:
TestStep
):
step
is
TestStep_CREATE
=>
{
export
const
isTestStep_CREATE
=
(
step
:
TestStep
):
step
is
TestStep_CREATE
=>
{
return
step
.
functionName
==
'
ovmCREATE
'
return
step
.
functionName
==
=
'
ovmCREATE
'
}
}
export
const
isTestStep_CREATE2
=
(
export
const
isTestStep_CREATE2
=
(
step
:
TestStep
step
:
TestStep
):
step
is
TestStep_CREATE2
=>
{
):
step
is
TestStep_CREATE2
=>
{
return
step
.
functionName
==
'
ovmCREATE2
'
return
step
.
functionName
===
'
ovmCREATE2
'
}
export
const
isTestStep_Run
=
(
step
:
TestStep
|
TestStep_Run
):
step
is
TestStep_Run
=>
{
return
step
.
functionName
===
'
run
'
}
}
interface
TestState
{
interface
TestState
{
...
@@ -216,7 +236,7 @@ interface TestState {
...
@@ -216,7 +236,7 @@ interface TestState {
export
interface
TestParameter
{
export
interface
TestParameter
{
name
:
string
name
:
string
steps
:
TestStep
[]
steps
:
Array
<
TestStep
|
TestStep_Run
>
expectInvalidStateAccess
?:
boolean
expectInvalidStateAccess
?:
boolean
focus
?:
boolean
focus
?:
boolean
}
}
...
...
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