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
Hide 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 {
)
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
// 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,7 +366,14 @@ contract OVM_StateManager is iOVM_StateManager {
authenticated
{
contractStorage[_contract][_key] = _value;
verifiedContractStorage[_contract][_key] = true;
// 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;
}
}
/**
...
...
packages/contracts/package.json
View file @
8d8e72dd
...
...
@@ -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/
ovmDELEGATECALL
.spec.ts
\"
"
,
"test:contracts"
:
"buidler test
\"
test/contracts/OVM/execution/OVM_ExecutionManager/
run
.spec.ts
\"
"
,
"lint"
:
"tslint --format stylish --project ."
,
"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,29 +166,57 @@ export const setContractStorage = async (
const
inputSlots
=
parseInputSlots
(
layout
,
layoutMap
.
type
)
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
'
)
{
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
(
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
)
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
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
[
subKey
]:
subValue
,
})
const
slotValues
=
getFlattenedValues
(
depth
,
{
[
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
)
}
}
}
}
...
...
@@ -223,28 +251,62 @@ export const checkContractStorage = async (
)
}
}
else
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
[
subKey
]:
subValue
,
})
const
slotValues
=
getFlattenedValues
(
depth
,
{
[
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
}
).`
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
{
for
(
const
[
subKey
,
subValue
]
of
Object
.
entries
(
value
))
{
const
baseSlotHash
=
getStorageSlotHash
(
slot
,
depth
,
{
[
subKey
]:
subValue
,
})
const
slotValues
=
getFlattenedValues
(
depth
,
{
[
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
}
).`
)
}
}
}
}
...
...
packages/contracts/test/helpers/test-utils/test-runner.ts
View file @
8d8e72dd
...
...
@@ -8,17 +8,20 @@ import { cloneDeep } from 'lodash'
/* Internal Imports */
import
{
TestDefinition
,
ParsedTestStep
,
TestParameter
,
TestStep
,
TestStep_CALL
,
TestStep_Run
,
isRevertFlagError
,
isTestStep_SSTORE
,
isTestStep_SLOAD
,
isTestStep_CALL
,
isTestStep_CREATE
,
isTestStep_CREATE2
,
isTestStep_Context
,
ParsedTestStep
,
isRevertFlagError
,
TestParameter
,
isTestStep_evm
,
isTestStep_Run
,
isTestStep_EXTCODESIZE
,
isTestStep_EXTCODEHASH
,
isTestStep_EXTCODECOPY
,
...
...
@@ -190,15 +193,47 @@ export class ExecutionManagerTestRunner {
return
ret
}
private
async
runTestStep
(
step
:
TestStep
)
{
await
this
.
contracts
.
OVM_ExecutionManager
.
ovmCALL
(
GAS_LIMIT
/
2
,
this
.
contracts
.
Helper_TestRunner
.
address
,
this
.
contracts
.
Helper_TestRunner
.
interface
.
encodeFunctionData
(
'
runSingleTestStep
'
,
[
this
.
parseTestStep
(
step
)]
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
(
GAS_LIMIT
/
2
,
this
.
contracts
.
Helper_TestRunner
.
address
,
this
.
contracts
.
Helper_TestRunner
.
interface
.
encodeFunctionData
(
'
runSingleTestStep
'
,
[
this
.
parseTestStep
(
step
)]
)
)
}
}
private
parseTestStep
(
step
:
TestStep
):
ParsedTestStep
{
...
...
packages/contracts/test/helpers/test-utils/test.types.ts
View file @
8d8e72dd
...
...
@@ -83,7 +83,7 @@ interface TestStep_SLOAD {
expectedReturnValue
:
string
|
RevertFlagError
}
interface
TestStep_CALL
{
export
interface
TestStep_CALL
{
functionName
:
CallOpcode
functionParams
:
{
gasLimit
:
number
|
BigNumber
...
...
@@ -116,6 +116,20 @@ interface TestStep_CREATE2 {
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
=
|
TestStep_Context
|
TestStep_SSTORE
...
...
@@ -164,33 +178,33 @@ export const isTestStep_Context = (
}
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
=>
{
return
step
.
functionName
==
'
ovmSLOAD
'
return
step
.
functionName
==
=
'
ovmSLOAD
'
}
export
const
isTestStep_EXTCODESIZE
=
(
step
:
TestStep
):
step
is
TestStep_EXTCODESIZE
=>
{
return
step
.
functionName
==
'
ovmEXTCODESIZE
'
return
step
.
functionName
==
=
'
ovmEXTCODESIZE
'
}
export
const
isTestStep_EXTCODEHASH
=
(
step
:
TestStep
):
step
is
TestStep_EXTCODEHASH
=>
{
return
step
.
functionName
==
'
ovmEXTCODEHASH
'
return
step
.
functionName
==
=
'
ovmEXTCODEHASH
'
}
export
const
isTestStep_EXTCODECOPY
=
(
step
:
TestStep
):
step
is
TestStep_EXTCODECOPY
=>
{
return
step
.
functionName
==
'
ovmEXTCODECOPY
'
return
step
.
functionName
==
=
'
ovmEXTCODECOPY
'
}
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
=>
{
...
...
@@ -200,13 +214,19 @@ export const isTestStep_CALL = (step: TestStep): step is TestStep_CALL => {
}
export
const
isTestStep_CREATE
=
(
step
:
TestStep
):
step
is
TestStep_CREATE
=>
{
return
step
.
functionName
==
'
ovmCREATE
'
return
step
.
functionName
==
=
'
ovmCREATE
'
}
export
const
isTestStep_CREATE2
=
(
step
:
TestStep
):
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
{
...
...
@@ -216,7 +236,7 @@ interface TestState {
export
interface
TestParameter
{
name
:
string
steps
:
TestStep
[]
steps
:
Array
<
TestStep
|
TestStep_Run
>
expectInvalidStateAccess
?:
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