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
c030d854
Unverified
Commit
c030d854
authored
Dec 07, 2022
by
protolambda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-wheel: cheat commands logic
parent
fe7509da
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
340 additions
and
0 deletions
+340
-0
cheat.go
op-wheel/cheat/cheat.go
+340
-0
No files found.
op-wheel/cheat/cheat.go
0 → 100644
View file @
c030d854
package
cheat
import
(
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"math/big"
"path/filepath"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
var
HundredETH
=
big
.
NewInt
(
0
)
.
Mul
(
big
.
NewInt
(
100
),
big
.
NewInt
(
1000000000000000000
))
type
Cheater
struct
{
// The database of the chain with the head block that we patch the state-root of, once the state is updated.
DB
ethdb
.
Database
// Initialized chain, wrapping the database with in-memory presentation of headers and recent changes and such.
Blockchain
*
core
.
BlockChain
// The Cheater avoids making writes if this is set to True, and opens the DB as readonly.
ReadOnly
bool
}
func
OpenGethRawDB
(
dataDirPath
string
,
readOnly
bool
)
(
ethdb
.
Database
,
error
)
{
// don't use readonly mode in actual DB, it doesn't work with Geth.
db
,
err
:=
rawdb
.
NewLevelDBDatabaseWithFreezer
(
dataDirPath
,
2048
,
500
,
filepath
.
Join
(
dataDirPath
,
"ancient"
),
""
,
readOnly
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to open leveldb: %w"
,
err
)
}
return
db
,
nil
}
// OpenGethDB opens a geth database to apply cheats to.
func
OpenGethDB
(
dataDirPath
string
,
readOnly
bool
)
(
*
Cheater
,
error
)
{
db
,
err
:=
OpenGethRawDB
(
dataDirPath
,
readOnly
)
if
err
!=
nil
{
return
nil
,
err
}
ch
,
err
:=
core
.
NewBlockChain
(
db
,
nil
,
nil
,
nil
,
beacon
.
New
(
ethash
.
NewFullFaker
()),
vm
.
Config
{},
nil
,
nil
)
if
err
!=
nil
{
_
=
db
.
Close
()
return
nil
,
fmt
.
Errorf
(
"failed to open blockchain around chain db: %w"
,
err
)
}
return
&
Cheater
{
DB
:
db
,
Blockchain
:
ch
,
ReadOnly
:
readOnly
,
},
nil
}
func
(
ch
*
Cheater
)
Close
()
error
{
return
ch
.
DB
.
Close
()
}
type
HeadFn
func
(
headState
*
state
.
StateDB
)
error
// RunAndClose runs the given function on the head-state, and then persists any changes (if not ReadOnly),
// and updates the blockchain headers indexes to reflect the new state-root, so geth will believe the cheat
// (unless it ever re-applies the block).
func
(
ch
*
Cheater
)
RunAndClose
(
fn
HeadFn
)
error
{
preBlock
:=
ch
.
Blockchain
.
CurrentBlock
()
if
a
,
b
:=
preBlock
.
NumberU64
(),
ch
.
Blockchain
.
Genesis
()
.
NumberU64
();
a
<=
b
{
return
fmt
.
Errorf
(
"cheating at genesis (head block %d <= genesis block %d) is not supported"
,
a
,
b
)
}
state
,
err
:=
ch
.
Blockchain
.
StateAt
(
preBlock
.
Root
())
if
err
!=
nil
{
_
=
ch
.
Close
()
return
fmt
.
Errorf
(
"failed to look up head state: %w"
,
err
)
}
if
err
:=
fn
(
state
);
err
!=
nil
{
_
=
ch
.
Close
()
return
fmt
.
Errorf
(
"failed to run state change: %w"
,
err
)
}
if
ch
.
ReadOnly
{
return
ch
.
Close
()
}
// commit the changes, and then update the state-root
stateRoot
,
err
:=
state
.
Commit
(
true
)
if
err
!=
nil
{
_
=
ch
.
Close
()
return
fmt
.
Errorf
(
"failed to commit state change: %w"
,
err
)
}
header
:=
preBlock
.
Header
()
header
.
Root
=
stateRoot
blockHash
:=
header
.
Hash
()
// We have to manually commit the updated state root to the database.
if
err
:=
state
.
Database
()
.
TrieDB
()
.
Commit
(
stateRoot
,
true
,
nil
);
err
!=
nil
{
return
fmt
.
Errorf
(
"error committing trie db: %w"
,
err
)
}
// based on core.BlockChain.writeHeadBlock:
// Add the block to the canonical chain number scheme and mark as the head
batch
:=
ch
.
DB
.
NewBatch
()
if
ch
.
Blockchain
.
CurrentFinalizedBlock
()
.
Hash
()
==
preBlock
.
Hash
()
{
rawdb
.
WriteFinalizedBlockHash
(
batch
,
blockHash
)
}
rawdb
.
DeleteHeaderNumber
(
batch
,
preBlock
.
Hash
())
rawdb
.
WriteHeadHeaderHash
(
batch
,
blockHash
)
rawdb
.
WriteHeadFastBlockHash
(
batch
,
blockHash
)
rawdb
.
WriteCanonicalHash
(
batch
,
blockHash
,
preBlock
.
NumberU64
())
rawdb
.
WriteHeaderNumber
(
batch
,
blockHash
,
preBlock
.
NumberU64
())
rawdb
.
WriteHeader
(
batch
,
header
)
// not keyed by blockhash, and we didn't remove any txs, so we just leave this one as-is.
// rawdb.WriteTxLookupEntriesByBlock(batch, block)
rawdb
.
WriteHeadBlockHash
(
batch
,
blockHash
)
// Geth stores the TD for each block separately from the block itself. We must update this
// manually, otherwise Geth thinks we haven't reached TTD yet and tries to build a block
// using Clique consensus, which causes a panic.
rawdb
.
WriteTd
(
batch
,
blockHash
,
preBlock
.
NumberU64
(),
ch
.
Blockchain
.
GetTd
(
preBlock
.
Hash
(),
preBlock
.
NumberU64
()))
// Geth maintains an internal mapping between block bodies and their hashes. None of the database
// accessors above update this mapping, so we need to do it manually.
oldKey
:=
blockBodyKey
(
preBlock
.
NumberU64
(),
preBlock
.
Hash
())
oldBody
:=
rawdb
.
ReadBodyRLP
(
ch
.
DB
,
preBlock
.
Hash
(),
preBlock
.
NumberU64
())
newKey
:=
blockBodyKey
(
preBlock
.
NumberU64
(),
blockHash
)
if
err
:=
batch
.
Delete
(
oldKey
);
err
!=
nil
{
return
fmt
.
Errorf
(
"error deleting old block body key"
)
}
if
err
:=
batch
.
Put
(
newKey
,
oldBody
);
err
!=
nil
{
return
fmt
.
Errorf
(
"error setting new block body key"
)
}
// Flush the whole batch into the disk, exit the node if failed
if
err
:=
batch
.
Write
();
err
!=
nil
{
_
=
ch
.
Close
()
return
fmt
.
Errorf
(
"failed to update chain indexes and markers: %w"
,
err
)
}
// Technically there are more in-memory things to update in real geth,
// to which we don't even have public API access, but that's fine, we're done.
// *And we did update the finalized marker, which is flushed from memory to disk on shutdown in geth.
// bc.hc.SetCurrentHeader(block.Header())
// headFastBlockGauge.Update(int64(block.NumberU64()))
// headBlockGauge.Update(int64(block.NumberU64()))
return
ch
.
Close
()
}
// StorageSet modifies the storage of the given address at the given key to the given value.
func
StorageSet
(
address
common
.
Address
,
key
common
.
Hash
,
value
common
.
Hash
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
headState
.
SetState
(
address
,
key
,
value
)
return
nil
}
}
// StorageGet just reads the storage of the given address at the given key.
func
StorageGet
(
address
common
.
Address
,
key
common
.
Hash
,
w
io
.
Writer
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
value
:=
headState
.
GetState
(
address
,
key
)
_
,
err
:=
io
.
WriteString
(
w
,
value
.
Hex
())
return
err
}
}
// StorageReadAll reads all values of the given address, and writes it as a (+) diff to the given output writer.
// Simply replace the (+) with (-) if you need to apply the diff as removal of values.
// Combined with StoragePatch this allows for quick surgery of 1 account in one database,
// to another account (maybe even in a different database!).
func
StorageReadAll
(
address
common
.
Address
,
w
io
.
Writer
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
storage
:=
headState
.
StorageTrie
(
address
)
iter
:=
trie
.
NewIterator
(
storage
.
NodeIterator
(
nil
))
for
iter
.
Next
()
{
if
_
,
err
:=
fmt
.
Fprintf
(
w
,
"+ %x = %x
\n
"
,
iter
.
Key
,
dbValueToHash
(
iter
.
Value
));
err
!=
nil
{
return
err
}
}
return
nil
}
}
func
dbValueToHash
(
enc
[]
byte
)
common
.
Hash
{
var
value
common
.
Hash
if
len
(
enc
)
>
0
{
_
,
content
,
_
,
err
:=
rlp
.
Split
(
enc
)
if
err
!=
nil
{
panic
(
err
)
}
value
.
SetBytes
(
content
)
}
return
value
}
// StorageDiff compares the storage of two different accounts, and writes a patch with differences.
// Each difference is expressed with 1 character + or - to indicate the change from a to b, followed by key = value.
func
StorageDiff
(
out
io
.
Writer
,
addressA
,
addressB
common
.
Address
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
aStorage
:=
headState
.
StorageTrie
(
addressA
)
bStorage
:=
headState
.
StorageTrie
(
addressB
)
aIter
:=
trie
.
NewIterator
(
aStorage
.
NodeIterator
(
nil
))
bIter
:=
trie
.
NewIterator
(
bStorage
.
NodeIterator
(
nil
))
hasA
:=
aIter
.
Next
()
hasB
:=
bIter
.
Next
()
for
{
if
!
hasA
&&
!
hasB
{
break
}
if
cmp
:=
bytes
.
Compare
(
aIter
.
Key
,
bIter
.
Key
);
cmp
<
0
{
// a is smaller, and thus missing in b. Print and move forward a
if
_
,
err
:=
fmt
.
Fprintf
(
out
,
"- %x = %x
\n
"
,
aIter
.
Key
,
dbValueToHash
(
aIter
.
Value
));
err
!=
nil
{
return
err
}
hasA
=
aIter
.
Next
()
}
else
if
cmp
>
0
{
// b is smaller, and thus missing in a. Print and move forward b
if
_
,
err
:=
fmt
.
Fprintf
(
out
,
"+ %x = %x
\n
"
,
bIter
.
Key
,
dbValueToHash
(
bIter
.
Value
));
err
!=
nil
{
return
err
}
hasB
=
bIter
.
Next
()
}
else
if
cmp
==
0
{
// same key, now check if the values differ
if
!
bytes
.
Equal
(
aIter
.
Value
,
bIter
.
Value
)
{
if
_
,
err
:=
fmt
.
Fprintf
(
out
,
"- %x = %x
\n
"
,
aIter
.
Key
,
dbValueToHash
(
aIter
.
Value
));
err
!=
nil
{
return
err
}
if
_
,
err
:=
fmt
.
Fprintf
(
out
,
"+ %x = %x
\n
"
,
bIter
.
Key
,
dbValueToHash
(
bIter
.
Value
));
err
!=
nil
{
return
err
}
}
// move both
hasA
=
aIter
.
Next
()
hasB
=
bIter
.
Next
()
}
}
return
nil
}
}
// StoragePatch applies a patch of changes to the given state account trie.
// Changes are hex encoded key-value pairs separated by (=).
// Additions are prefixed with (+).
// Deletions are prefixed with (-) and overwrite it to a zero value.
// Comments (#) and empty lines are ignored.
func
StoragePatch
(
patch
io
.
Reader
,
address
common
.
Address
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
s
:=
bufio
.
NewScanner
(
patch
)
i
:=
0
for
s
.
Scan
()
{
line
:=
s
.
Text
()
if
len
(
line
)
<
1
||
line
[
0
]
==
'#'
{
// skip empty lines and comments
continue
}
parts
:=
strings
.
Split
(
line
[
1
:
],
"="
)
keyHex
:=
strings
.
TrimSpace
(
parts
[
0
])
valueHex
:=
strings
.
TrimSpace
(
parts
[
1
])
var
key
,
value
common
.
Hash
if
err
:=
key
.
UnmarshalText
([]
byte
(
keyHex
));
err
!=
nil
{
return
fmt
.
Errorf
(
"key %s is malformatted: %w"
,
keyHex
,
err
)
}
if
err
:=
value
.
UnmarshalText
([]
byte
(
valueHex
));
err
!=
nil
{
return
fmt
.
Errorf
(
"key %s has malformatted value %s: %w"
,
keyHex
,
valueHex
,
err
)
}
switch
line
[
0
]
{
case
'+'
:
headState
.
SetState
(
address
,
key
,
value
)
case
'-'
:
headState
.
SetState
(
address
,
key
,
common
.
Hash
{})
default
:
return
fmt
.
Errorf
(
"unrecognized line diff token"
)
}
i
+=
1
if
i
%
1000
==
0
{
// for every 1000 values, commit to disk
if
_
,
err
:=
headState
.
Commit
(
true
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to commit state to disk after patching %d entries: %w"
,
i
,
err
)
}
}
}
return
nil
}
}
type
OvmOwnersConfig
struct
{
Owner
common
.
Address
`json:"owner"`
Sequencer
common
.
Address
`json:"sequencer"`
Proposer
common
.
Address
`json:"proposer"`
}
func
OvmOwners
(
conf
*
OvmOwnersConfig
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
// Address manager owner
headState
.
SetState
(
common
.
HexToAddress
(
"0xa6f73589243a6A7a9023b1Fa0651b1d89c177111"
),
common
.
Hash
{},
conf
.
Owner
.
Hash
())
// L1SB proxy owner
headState
.
SetState
(
common
.
HexToAddress
(
"0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"
),
common
.
HexToHash
(
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"
),
conf
.
Owner
.
Hash
())
// L1XDM owner
headState
.
SetState
(
common
.
HexToAddress
(
"0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"
),
common
.
Hash
{
31
:
0x33
},
conf
.
Owner
.
Hash
())
// L1 ERC721 bridge owner
headState
.
SetState
(
common
.
HexToAddress
(
"0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9"
),
common
.
HexToHash
(
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"
),
conf
.
Owner
.
Hash
())
// Legacy sequencer/proposer addresses
headState
.
SetState
(
common
.
HexToAddress
(
"0xa6f73589243a6A7a9023b1Fa0651b1d89c177111"
),
common
.
HexToHash
(
"0x2e0dfce60e9e27f035ce28f63c1bdd77cff6b13d8909da4d81d623ff9123fbdc"
),
conf
.
Sequencer
.
Hash
())
headState
.
SetState
(
common
.
HexToAddress
(
"0xa6f73589243a6A7a9023b1Fa0651b1d89c177111"
),
common
.
HexToHash
(
"0x9776dbdebd0d5eedaea450b21da9901ecd5254e5136a3a9b7b0ecd532734d5b5"
),
conf
.
Proposer
.
Hash
())
// Fund sequencer and proposer with 100 ETH
headState
.
SetBalance
(
conf
.
Sequencer
,
HundredETH
)
headState
.
SetBalance
(
conf
.
Proposer
,
HundredETH
)
return
nil
}
}
func
SetBalance
(
addr
common
.
Address
,
amount
*
big
.
Int
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
headState
.
SetBalance
(
addr
,
amount
)
return
nil
}
}
func
SetNonce
(
addr
common
.
Address
,
nonce
uint64
)
HeadFn
{
return
func
(
headState
*
state
.
StateDB
)
error
{
headState
.
SetNonce
(
addr
,
nonce
)
return
nil
}
}
// blockBodyKey returns the database key to use for storing the body of a block.
// This function was copied from Geth's core/rawdb/accessors_chain.go.
func
blockBodyKey
(
number
uint64
,
hash
common
.
Hash
)
[]
byte
{
return
append
(
append
([]
byte
(
"b"
),
encodeBlockNumber
(
number
)
...
),
hash
.
Bytes
()
...
)
}
// encodeBlockNumber encodes a block number as big endian uint64. This function was
// copied from Geth's core/rawdb/accessors_chain.go file.
func
encodeBlockNumber
(
number
uint64
)
[]
byte
{
enc
:=
make
([]
byte
,
8
)
binary
.
BigEndian
.
PutUint64
(
enc
,
number
)
return
enc
}
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