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
60a349f4
Commit
60a349f4
authored
Sep 22, 2021
by
George Hotz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
close to compiling with stock state_object
parent
018ee56a
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
711 additions
and
122 deletions
+711
-122
database.go
minigeth/core/state/database.go
+85
-5
journal.go
minigeth/core/state/journal.go
+269
-0
snapshot.go
minigeth/core/state/snapshot/snapshot.go
+14
-0
state_object.go
minigeth/core/state/state_object.go
+294
-107
statedb.go
minigeth/core/state/statedb.go
+46
-10
metrics.go
minigeth/metrics/metrics.go
+3
-0
No files found.
minigeth/core/state/database.go
View file @
60a349f4
...
...
@@ -4,6 +4,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/oracle"
)
...
...
@@ -15,24 +16,103 @@ type Database struct {
StateRoot
common
.
Hash
}
var
unhashMap
=
make
(
map
[
common
.
Hash
]
common
.
Address
)
func
unhash
(
addrHash
common
.
Hash
)
common
.
Address
{
return
unhashMap
[
addrHash
]
}
// ContractCode retrieves a particular contract's code.
func
(
db
*
Database
)
ContractCode
(
addr
common
.
Address
,
codeHash
common
.
Hash
)
([]
byte
,
error
)
{
func
(
db
*
Database
)
ContractCode
(
addrHash
common
.
Hash
,
codeHash
common
.
Hash
)
([]
byte
,
error
)
{
addr
:=
unhash
(
addrHash
)
code
:=
oracle
.
GetProvedCodeBytes
(
db
.
BlockNumber
,
addr
,
codeHash
)
return
code
,
nil
}
func
(
db
*
Database
)
CopyTrie
(
trie
Trie
)
Trie
{
// TODO: this is wrong
return
trie
}
// ContractCodeSize retrieves a particular contracts code's size.
func
(
db
*
Database
)
ContractCodeSize
(
addr
common
.
Address
,
codeHash
common
.
Hash
)
(
int
,
error
)
{
func
(
db
*
Database
)
ContractCodeSize
(
addrHash
common
.
Hash
,
codeHash
common
.
Hash
)
(
int
,
error
)
{
addr
:=
unhash
(
addrHash
)
code
:=
oracle
.
GetProvedCodeBytes
(
db
.
BlockNumber
,
addr
,
codeHash
)
return
len
(
code
),
nil
}
type
Trie
struct
{
// OpenStorageTrie opens the storage trie of an account.
func
(
db
*
Database
)
OpenStorageTrie
(
addrHash
,
root
common
.
Hash
)
(
Trie
,
error
)
{
return
simpleTrie
{},
nil
}
type
LeafCallback
func
(
paths
[][]
byte
,
hexpath
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
)
error
type
Trie
interface
{
// TryGet returns the value for key stored in the trie. The value bytes must
// not be modified by the caller. If a node was not found in the database, a
// trie.MissingNodeError is returned.
TryGet
(
key
[]
byte
)
([]
byte
,
error
)
// TryUpdate associates key with value in the trie. If value has length zero, any
// existing value is deleted from the trie. The value bytes must not be modified
// by the caller while they are stored in the trie. If a node was not found in the
// database, a trie.MissingNodeError is returned.
TryUpdate
(
key
,
value
[]
byte
)
error
// TryDelete removes any existing value for key from the trie. If a node was not
// found in the database, a trie.MissingNodeError is returned.
TryDelete
(
key
[]
byte
)
error
// Hash returns the root hash of the trie. It does not write to the database and
// can be used even if the trie doesn't have one.
Hash
()
common
.
Hash
// Commit writes all nodes to the trie's memory database, tracking the internal
// and external (for account tries) references.
Commit
(
onleaf
LeafCallback
)
(
common
.
Hash
,
error
)
}
type
simpleTrie
struct
{
BlockNumber
*
big
.
Int
StateRoot
common
.
Hash
}
func
(
trie
*
Trie
)
TryGet
(
key
[]
byte
)
([]
byte
,
error
)
{
enc
:=
oracle
.
GetProvedAccountBytes
(
trie
.
BlockNumber
,
trie
.
StateRoot
,
common
.
BytesToAddress
(
key
))
func
(
trie
simpleTrie
)
Commit
(
onleaf
LeafCallback
)
(
common
.
Hash
,
error
)
{
return
trie
.
StateRoot
,
nil
}
func
(
trie
simpleTrie
)
Hash
()
common
.
Hash
{
return
trie
.
StateRoot
}
func
(
trie
simpleTrie
)
TryUpdate
(
key
,
value
[]
byte
)
error
{
return
nil
}
func
(
trie
simpleTrie
)
TryDelete
(
key
[]
byte
)
error
{
return
nil
}
func
(
trie
simpleTrie
)
TryGet
(
key
[]
byte
)
([]
byte
,
error
)
{
address
:=
common
.
BytesToAddress
(
key
)
addrHash
:=
crypto
.
Keccak256Hash
(
address
[
:
])
unhashMap
[
addrHash
]
=
address
enc
:=
oracle
.
GetProvedAccountBytes
(
trie
.
BlockNumber
,
trie
.
StateRoot
,
address
)
return
enc
,
nil
}
// stubbed: we don't prefetch
type
triePrefetcher
struct
{
}
func
(
p
*
triePrefetcher
)
prefetch
(
root
common
.
Hash
,
keys
[][]
byte
)
{
}
func
(
p
*
triePrefetcher
)
used
(
root
common
.
Hash
,
used
[][]
byte
)
{
}
func
(
p
*
triePrefetcher
)
trie
(
root
common
.
Hash
)
Trie
{
return
nil
}
minigeth/core/state/journal.go
0 → 100644
View file @
60a349f4
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
state
import
(
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// journalEntry is a modification entry in the state change journal that can be
// reverted on demand.
type
journalEntry
interface
{
// revert undoes the changes introduced by this journal entry.
revert
(
*
StateDB
)
// dirtied returns the Ethereum address modified by this journal entry.
dirtied
()
*
common
.
Address
}
// journal contains the list of state modifications applied since the last state
// commit. These are tracked to be able to be reverted in case of an execution
// exception or revertal request.
type
journal
struct
{
entries
[]
journalEntry
// Current changes tracked by the journal
dirties
map
[
common
.
Address
]
int
// Dirty accounts and the number of changes
}
// newJournal create a new initialized journal.
func
newJournal
()
*
journal
{
return
&
journal
{
dirties
:
make
(
map
[
common
.
Address
]
int
),
}
}
// append inserts a new modification entry to the end of the change journal.
func
(
j
*
journal
)
append
(
entry
journalEntry
)
{
j
.
entries
=
append
(
j
.
entries
,
entry
)
if
addr
:=
entry
.
dirtied
();
addr
!=
nil
{
j
.
dirties
[
*
addr
]
++
}
}
// revert undoes a batch of journalled modifications along with any reverted
// dirty handling too.
func
(
j
*
journal
)
revert
(
statedb
*
StateDB
,
snapshot
int
)
{
for
i
:=
len
(
j
.
entries
)
-
1
;
i
>=
snapshot
;
i
--
{
// Undo the changes made by the operation
j
.
entries
[
i
]
.
revert
(
statedb
)
// Drop any dirty tracking induced by the change
if
addr
:=
j
.
entries
[
i
]
.
dirtied
();
addr
!=
nil
{
if
j
.
dirties
[
*
addr
]
--
;
j
.
dirties
[
*
addr
]
==
0
{
delete
(
j
.
dirties
,
*
addr
)
}
}
}
j
.
entries
=
j
.
entries
[
:
snapshot
]
}
// dirty explicitly sets an address to dirty, even if the change entries would
// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD
// precompile consensus exception.
func
(
j
*
journal
)
dirty
(
addr
common
.
Address
)
{
j
.
dirties
[
addr
]
++
}
// length returns the current number of entries in the journal.
func
(
j
*
journal
)
length
()
int
{
return
len
(
j
.
entries
)
}
type
(
// Changes to the account trie.
createObjectChange
struct
{
account
*
common
.
Address
}
resetObjectChange
struct
{
prev
*
stateObject
prevdestruct
bool
}
suicideChange
struct
{
account
*
common
.
Address
prev
bool
// whether account had already suicided
prevbalance
*
big
.
Int
}
// Changes to individual accounts.
balanceChange
struct
{
account
*
common
.
Address
prev
*
big
.
Int
}
nonceChange
struct
{
account
*
common
.
Address
prev
uint64
}
storageChange
struct
{
account
*
common
.
Address
key
,
prevalue
common
.
Hash
}
codeChange
struct
{
account
*
common
.
Address
prevcode
,
prevhash
[]
byte
}
// Changes to other state values.
refundChange
struct
{
prev
uint64
}
addLogChange
struct
{
txhash
common
.
Hash
}
addPreimageChange
struct
{
hash
common
.
Hash
}
touchChange
struct
{
account
*
common
.
Address
}
// Changes to the access list
accessListAddAccountChange
struct
{
address
*
common
.
Address
}
accessListAddSlotChange
struct
{
address
*
common
.
Address
slot
*
common
.
Hash
}
)
func
(
ch
createObjectChange
)
revert
(
s
*
StateDB
)
{
delete
(
s
.
stateObjects
,
*
ch
.
account
)
delete
(
s
.
stateObjectsDirty
,
*
ch
.
account
)
}
func
(
ch
createObjectChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
resetObjectChange
)
revert
(
s
*
StateDB
)
{
s
.
setStateObject
(
ch
.
prev
)
if
!
ch
.
prevdestruct
&&
s
.
snap
!=
nil
{
delete
(
s
.
snapDestructs
,
ch
.
prev
.
addrHash
)
}
}
func
(
ch
resetObjectChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
suicideChange
)
revert
(
s
*
StateDB
)
{
obj
:=
s
.
getStateObject
(
*
ch
.
account
)
if
obj
!=
nil
{
obj
.
suicided
=
ch
.
prev
obj
.
setBalance
(
ch
.
prevbalance
)
}
}
func
(
ch
suicideChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
var
ripemd
=
common
.
HexToAddress
(
"0000000000000000000000000000000000000003"
)
func
(
ch
touchChange
)
revert
(
s
*
StateDB
)
{
}
func
(
ch
touchChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
balanceChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setBalance
(
ch
.
prev
)
}
func
(
ch
balanceChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
nonceChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setNonce
(
ch
.
prev
)
}
func
(
ch
nonceChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
codeChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setCode
(
common
.
BytesToHash
(
ch
.
prevhash
),
ch
.
prevcode
)
}
func
(
ch
codeChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
storageChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setState
(
ch
.
key
,
ch
.
prevalue
)
}
func
(
ch
storageChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
refundChange
)
revert
(
s
*
StateDB
)
{
s
.
refund
=
ch
.
prev
}
func
(
ch
refundChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
addLogChange
)
revert
(
s
*
StateDB
)
{
logs
:=
s
.
logs
[
ch
.
txhash
]
if
len
(
logs
)
==
1
{
delete
(
s
.
logs
,
ch
.
txhash
)
}
else
{
s
.
logs
[
ch
.
txhash
]
=
logs
[
:
len
(
logs
)
-
1
]
}
s
.
logSize
--
}
func
(
ch
addLogChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
addPreimageChange
)
revert
(
s
*
StateDB
)
{
delete
(
s
.
preimages
,
ch
.
hash
)
}
func
(
ch
addPreimageChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
accessListAddAccountChange
)
revert
(
s
*
StateDB
)
{
/*
One important invariant here, is that whenever a (addr, slot) is added, if the
addr is not already present, the add causes two journal entries:
- one for the address,
- one for the (address,slot)
Therefore, when unrolling the change, we can always blindly delete the
(addr) at this point, since no storage adds can remain when come upon
a single (addr) change.
*/
s
.
accessList
.
DeleteAddress
(
*
ch
.
address
)
}
func
(
ch
accessListAddAccountChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
accessListAddSlotChange
)
revert
(
s
*
StateDB
)
{
s
.
accessList
.
DeleteSlot
(
*
ch
.
address
,
*
ch
.
slot
)
}
func
(
ch
accessListAddSlotChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
minigeth/core/state/snapshot/snapshot.go
0 → 100644
View file @
60a349f4
package
snapshot
import
"github.com/ethereum/go-ethereum/common"
// entirely stubs, this is never created
type
Tree
interface
{
}
type
Snapshot
interface
{
// Storage directly retrieves the storage data associated with a particular hash,
// within a particular account.
Storage
(
accountHash
,
storageHash
common
.
Hash
)
([]
byte
,
error
)
}
minigeth/core/state/state_object.go
View file @
60a349f4
...
...
@@ -21,10 +21,11 @@ import (
"fmt"
"io"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/
oracle
"
"github.com/ethereum/go-ethereum/
metrics
"
"github.com/ethereum/go-ethereum/rlp"
)
...
...
@@ -75,7 +76,7 @@ type stateObject struct {
dbErr
error
// Write caches.
//
trie Trie // storage trie, which becomes non-nil on first access
trie
Trie
// storage trie, which becomes non-nil on first access
code
Code
// contract bytecode, which gets set when code is loaded
originStorage
Storage
// Storage cache of original entries to dedup rewrites, reset for every transaction
...
...
@@ -143,6 +144,277 @@ func (s *stateObject) markSuicided() {
s
.
suicided
=
true
}
func
(
s
*
stateObject
)
touch
()
{
s
.
db
.
journal
.
append
(
touchChange
{
account
:
&
s
.
address
,
})
if
s
.
address
==
ripemd
{
// Explicitly put it in the dirty-cache, which is otherwise generated from
// flattened journals.
s
.
db
.
journal
.
dirty
(
s
.
address
)
}
}
func
(
s
*
stateObject
)
getTrie
(
db
Database
)
Trie
{
if
s
.
trie
==
nil
{
// Try fetching from prefetcher first
// We don't prefetch empty tries
if
s
.
data
.
Root
!=
emptyRoot
&&
s
.
db
.
prefetcher
!=
nil
{
// When the miner is creating the pending state, there is no
// prefetcher
s
.
trie
=
s
.
db
.
prefetcher
.
trie
(
s
.
data
.
Root
)
}
if
s
.
trie
==
nil
{
var
err
error
s
.
trie
,
err
=
db
.
OpenStorageTrie
(
s
.
addrHash
,
s
.
data
.
Root
)
if
err
!=
nil
{
s
.
trie
,
_
=
db
.
OpenStorageTrie
(
s
.
addrHash
,
common
.
Hash
{})
s
.
setError
(
fmt
.
Errorf
(
"can't create storage trie: %v"
,
err
))
}
}
}
return
s
.
trie
}
// GetState retrieves a value from the account storage trie.
func
(
s
*
stateObject
)
GetState
(
db
Database
,
key
common
.
Hash
)
common
.
Hash
{
// If the fake storage is set, only lookup the state here(in the debugging mode)
if
s
.
fakeStorage
!=
nil
{
return
s
.
fakeStorage
[
key
]
}
// If we have a dirty value for this state entry, return it
value
,
dirty
:=
s
.
dirtyStorage
[
key
]
if
dirty
{
return
value
}
// Otherwise return the entry's original value
return
s
.
GetCommittedState
(
db
,
key
)
}
// GetCommittedState retrieves a value from the committed account storage trie.
func
(
s
*
stateObject
)
GetCommittedState
(
db
Database
,
key
common
.
Hash
)
common
.
Hash
{
// If the fake storage is set, only lookup the state here(in the debugging mode)
if
s
.
fakeStorage
!=
nil
{
return
s
.
fakeStorage
[
key
]
}
// If we have a pending write or clean cached, return that
if
value
,
pending
:=
s
.
pendingStorage
[
key
];
pending
{
return
value
}
if
value
,
cached
:=
s
.
originStorage
[
key
];
cached
{
return
value
}
// If no live objects are available, attempt to use snapshots
var
(
enc
[]
byte
err
error
meter
*
time
.
Duration
)
readStart
:=
time
.
Now
()
if
metrics
.
EnabledExpensive
{
// If the snap is 'under construction', the first lookup may fail. If that
// happens, we don't want to double-count the time elapsed. Thus this
// dance with the metering.
defer
func
()
{
if
meter
!=
nil
{
*
meter
+=
time
.
Since
(
readStart
)
}
}()
}
if
s
.
db
.
snap
!=
nil
{
if
metrics
.
EnabledExpensive
{
meter
=
&
s
.
db
.
SnapshotStorageReads
}
// If the object was destructed in *this* block (and potentially resurrected),
// the storage has been cleared out, and we should *not* consult the previous
// snapshot about any storage values. The only possible alternatives are:
// 1) resurrect happened, and new slot values were set -- those should
// have been handles via pendingStorage above.
// 2) we don't have new values, and can deliver empty response back
if
_
,
destructed
:=
s
.
db
.
snapDestructs
[
s
.
addrHash
];
destructed
{
return
common
.
Hash
{}
}
enc
,
err
=
s
.
db
.
snap
.
Storage
(
s
.
addrHash
,
crypto
.
Keccak256Hash
(
key
.
Bytes
()))
}
// If snapshot unavailable or reading from it failed, load from the database
if
s
.
db
.
snap
==
nil
||
err
!=
nil
{
if
meter
!=
nil
{
// If we already spent time checking the snapshot, account for it
// and reset the readStart
*
meter
+=
time
.
Since
(
readStart
)
readStart
=
time
.
Now
()
}
if
metrics
.
EnabledExpensive
{
meter
=
&
s
.
db
.
StorageReads
}
if
enc
,
err
=
s
.
getTrie
(
db
)
.
TryGet
(
key
.
Bytes
());
err
!=
nil
{
s
.
setError
(
err
)
return
common
.
Hash
{}
}
}
var
value
common
.
Hash
if
len
(
enc
)
>
0
{
_
,
content
,
_
,
err
:=
rlp
.
Split
(
enc
)
if
err
!=
nil
{
s
.
setError
(
err
)
}
value
.
SetBytes
(
content
)
}
s
.
originStorage
[
key
]
=
value
return
value
}
// SetState updates a value in account storage.
func
(
s
*
stateObject
)
SetState
(
db
Database
,
key
,
value
common
.
Hash
)
{
// If the fake storage is set, put the temporary state update here.
if
s
.
fakeStorage
!=
nil
{
s
.
fakeStorage
[
key
]
=
value
return
}
// If the new value is the same as old, don't set
prev
:=
s
.
GetState
(
db
,
key
)
if
prev
==
value
{
return
}
// New value is different, update and journal the change
s
.
db
.
journal
.
append
(
storageChange
{
account
:
&
s
.
address
,
key
:
key
,
prevalue
:
prev
,
})
s
.
setState
(
key
,
value
)
}
// SetStorage replaces the entire state storage with the given one.
//
// After this function is called, all original state will be ignored and state
// lookup only happens in the fake state storage.
//
// Note this function should only be used for debugging purpose.
func
(
s
*
stateObject
)
SetStorage
(
storage
map
[
common
.
Hash
]
common
.
Hash
)
{
// Allocate fake storage if it's nil.
if
s
.
fakeStorage
==
nil
{
s
.
fakeStorage
=
make
(
Storage
)
}
for
key
,
value
:=
range
storage
{
s
.
fakeStorage
[
key
]
=
value
}
// Don't bother journal since this function should only be used for
// debugging and the `fake` storage won't be committed to database.
}
func
(
s
*
stateObject
)
setState
(
key
,
value
common
.
Hash
)
{
s
.
dirtyStorage
[
key
]
=
value
}
// finalise moves all dirty storage slots into the pending area to be hashed or
// committed later. It is invoked at the end of every transaction.
func
(
s
*
stateObject
)
finalise
(
prefetch
bool
)
{
slotsToPrefetch
:=
make
([][]
byte
,
0
,
len
(
s
.
dirtyStorage
))
for
key
,
value
:=
range
s
.
dirtyStorage
{
s
.
pendingStorage
[
key
]
=
value
if
value
!=
s
.
originStorage
[
key
]
{
slotsToPrefetch
=
append
(
slotsToPrefetch
,
common
.
CopyBytes
(
key
[
:
]))
// Copy needed for closure
}
}
if
s
.
db
.
prefetcher
!=
nil
&&
prefetch
&&
len
(
slotsToPrefetch
)
>
0
&&
s
.
data
.
Root
!=
emptyRoot
{
s
.
db
.
prefetcher
.
prefetch
(
s
.
data
.
Root
,
slotsToPrefetch
)
}
if
len
(
s
.
dirtyStorage
)
>
0
{
s
.
dirtyStorage
=
make
(
Storage
)
}
}
// updateTrie writes cached storage modifications into the object's storage trie.
// It will return nil if the trie has not been loaded and no changes have been made
func
(
s
*
stateObject
)
updateTrie
(
db
Database
)
Trie
{
// Make sure all dirty slots are finalized into the pending storage area
s
.
finalise
(
false
)
// Don't prefetch any more, pull directly if need be
if
len
(
s
.
pendingStorage
)
==
0
{
return
s
.
trie
}
// Track the amount of time wasted on updating the storage trie
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
db
.
StorageUpdates
+=
time
.
Since
(
start
)
}(
time
.
Now
())
}
// The snapshot storage map for the object
var
storage
map
[
common
.
Hash
][]
byte
// Insert all the pending updates into the trie
tr
:=
s
.
getTrie
(
db
)
hasher
:=
s
.
db
.
hasher
usedStorage
:=
make
([][]
byte
,
0
,
len
(
s
.
pendingStorage
))
for
key
,
value
:=
range
s
.
pendingStorage
{
// Skip noop changes, persist actual changes
if
value
==
s
.
originStorage
[
key
]
{
continue
}
s
.
originStorage
[
key
]
=
value
var
v
[]
byte
if
(
value
==
common
.
Hash
{})
{
s
.
setError
(
tr
.
TryDelete
(
key
[
:
]))
}
else
{
// Encoding []byte cannot fail, ok to ignore the error.
v
,
_
=
rlp
.
EncodeToBytes
(
common
.
TrimLeftZeroes
(
value
[
:
]))
s
.
setError
(
tr
.
TryUpdate
(
key
[
:
],
v
))
}
// If state snapshotting is active, cache the data til commit
if
s
.
db
.
snap
!=
nil
{
if
storage
==
nil
{
// Retrieve the old storage map, if available, create a new one otherwise
if
storage
=
s
.
db
.
snapStorage
[
s
.
addrHash
];
storage
==
nil
{
storage
=
make
(
map
[
common
.
Hash
][]
byte
)
s
.
db
.
snapStorage
[
s
.
addrHash
]
=
storage
}
}
storage
[
crypto
.
HashData
(
hasher
,
key
[
:
])]
=
v
// v will be nil if value is 0x00
}
usedStorage
=
append
(
usedStorage
,
common
.
CopyBytes
(
key
[
:
]))
// Copy needed for closure
}
if
s
.
db
.
prefetcher
!=
nil
{
s
.
db
.
prefetcher
.
used
(
s
.
data
.
Root
,
usedStorage
)
}
if
len
(
s
.
pendingStorage
)
>
0
{
s
.
pendingStorage
=
make
(
Storage
)
}
return
tr
}
// UpdateRoot sets the trie root to the current root hash of
func
(
s
*
stateObject
)
updateRoot
(
db
Database
)
{
// If nothing changed, don't bother with hashing anything
if
s
.
updateTrie
(
db
)
==
nil
{
return
}
// Track the amount of time wasted on hashing the storage trie
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
db
.
StorageHashes
+=
time
.
Since
(
start
)
}(
time
.
Now
())
}
s
.
data
.
Root
=
s
.
trie
.
Hash
()
}
// CommitTrie the storage trie of the object to db.
// This updates the trie root.
func
(
s
*
stateObject
)
CommitTrie
(
db
Database
)
error
{
// If nothing changed, don't bother with hashing anything
if
s
.
updateTrie
(
db
)
==
nil
{
return
nil
}
if
s
.
dbErr
!=
nil
{
return
s
.
dbErr
}
// Track the amount of time wasted on committing the storage trie
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
db
.
StorageCommits
+=
time
.
Since
(
start
)
}(
time
.
Now
())
}
root
,
err
:=
s
.
trie
.
Commit
(
nil
)
if
err
==
nil
{
s
.
data
.
Root
=
root
}
return
err
}
// AddBalance adds amount to s's balance.
// It is used to add funds to the destination account of a transfer.
func
(
s
*
stateObject
)
AddBalance
(
amount
*
big
.
Int
)
{
...
...
@@ -150,7 +422,7 @@ func (s *stateObject) AddBalance(amount *big.Int) {
// clearing (0,0,0 objects) can take effect.
if
amount
.
Sign
()
==
0
{
if
s
.
empty
()
{
//
s.touch()
s
.
touch
()
}
return
}
...
...
@@ -167,6 +439,10 @@ func (s *stateObject) SubBalance(amount *big.Int) {
}
func
(
s
*
stateObject
)
SetBalance
(
amount
*
big
.
Int
)
{
s
.
db
.
journal
.
append
(
balanceChange
{
account
:
&
s
.
address
,
prev
:
new
(
big
.
Int
)
.
Set
(
s
.
data
.
Balance
),
})
s
.
setBalance
(
amount
)
}
...
...
@@ -176,6 +452,9 @@ func (s *stateObject) setBalance(amount *big.Int) {
func
(
s
*
stateObject
)
deepCopy
(
db
*
StateDB
)
*
stateObject
{
stateObject
:=
newObject
(
db
,
s
.
address
,
s
.
data
)
if
s
.
trie
!=
nil
{
stateObject
.
trie
=
db
.
db
.
CopyTrie
(
s
.
trie
)
}
stateObject
.
code
=
s
.
code
stateObject
.
dirtyStorage
=
s
.
dirtyStorage
.
Copy
()
stateObject
.
originStorage
=
s
.
originStorage
.
Copy
()
...
...
@@ -196,7 +475,7 @@ func (s *stateObject) Address() common.Address {
}
// Code returns the contract code associated with this object, if any.
/*
func (s *stateObject) Code(db Database) []byte {
func
(
s
*
stateObject
)
Code
(
db
Database
)
[]
byte
{
if
s
.
code
!=
nil
{
return
s
.
code
}
...
...
@@ -226,9 +505,15 @@ func (s *stateObject) CodeSize(db Database) int {
s
.
setError
(
fmt
.
Errorf
(
"can't load code size %x: %v"
,
s
.
CodeHash
(),
err
))
}
return
size
}
*/
}
func
(
s
*
stateObject
)
SetCode
(
codeHash
common
.
Hash
,
code
[]
byte
)
{
prevcode
:=
s
.
Code
(
s
.
db
.
db
)
s
.
db
.
journal
.
append
(
codeChange
{
account
:
&
s
.
address
,
prevhash
:
s
.
CodeHash
(),
prevcode
:
prevcode
,
})
s
.
setCode
(
codeHash
,
code
)
}
...
...
@@ -239,6 +524,10 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
}
func
(
s
*
stateObject
)
SetNonce
(
nonce
uint64
)
{
s
.
db
.
journal
.
append
(
nonceChange
{
account
:
&
s
.
address
,
prev
:
s
.
data
.
Nonce
,
})
s
.
setNonce
(
nonce
)
}
...
...
@@ -264,105 +553,3 @@ func (s *stateObject) Nonce() uint64 {
func
(
s
*
stateObject
)
Value
()
*
big
.
Int
{
panic
(
"Value on stateObject should never be called"
)
}
// Code returns the contract code associated with this object, if any.
func
(
s
*
stateObject
)
Code
(
db
Database
)
[]
byte
{
if
s
.
code
!=
nil
{
return
s
.
code
}
if
bytes
.
Equal
(
s
.
CodeHash
(),
emptyCodeHash
)
{
return
nil
}
code
,
err
:=
db
.
ContractCode
(
s
.
address
,
common
.
BytesToHash
(
s
.
CodeHash
()))
if
err
!=
nil
{
s
.
setError
(
fmt
.
Errorf
(
"can't load code hash %x: %v"
,
s
.
CodeHash
(),
err
))
}
s
.
code
=
code
return
code
}
// CodeSize returns the size of the contract code associated with this object,
// or zero if none. This method is an almost mirror of Code, but uses a cache
// inside the database to avoid loading codes seen recently.
func
(
s
*
stateObject
)
CodeSize
(
db
Database
)
int
{
if
s
.
code
!=
nil
{
return
len
(
s
.
code
)
}
if
bytes
.
Equal
(
s
.
CodeHash
(),
emptyCodeHash
)
{
return
0
}
size
,
err
:=
db
.
ContractCodeSize
(
s
.
address
,
common
.
BytesToHash
(
s
.
CodeHash
()))
if
err
!=
nil
{
s
.
setError
(
fmt
.
Errorf
(
"can't load code size %x: %v"
,
s
.
CodeHash
(),
err
))
}
return
size
}
// GetState retrieves a value from the account storage trie.
func
(
s
*
stateObject
)
GetState
(
db
Database
,
key
common
.
Hash
)
common
.
Hash
{
// If the fake storage is set, only lookup the state here(in the debugging mode)
if
s
.
fakeStorage
!=
nil
{
return
s
.
fakeStorage
[
key
]
}
// If we have a dirty value for this state entry, return it
value
,
dirty
:=
s
.
dirtyStorage
[
key
]
if
dirty
{
return
value
}
// Otherwise return the entry's original value
return
s
.
GetCommittedState
(
db
,
key
)
}
// GetCommittedState retrieves a value from the committed account storage trie.
func
(
s
*
stateObject
)
GetCommittedState
(
db
Database
,
key
common
.
Hash
)
common
.
Hash
{
// If we have a pending write or clean cached, return that
if
value
,
pending
:=
s
.
pendingStorage
[
key
];
pending
{
return
value
}
if
value
,
cached
:=
s
.
originStorage
[
key
];
cached
{
return
value
}
value
:=
oracle
.
GetProvedStorage
(
db
.
BlockNumber
,
s
.
address
,
s
.
data
.
Root
,
key
)
s
.
originStorage
[
key
]
=
value
return
value
}
// SetState updates a value in account storage.
func
(
s
*
stateObject
)
SetState
(
db
Database
,
key
,
value
common
.
Hash
)
{
// If the fake storage is set, put the temporary state update here.
if
s
.
fakeStorage
!=
nil
{
s
.
fakeStorage
[
key
]
=
value
return
}
// If the new value is the same as old, don't set
prev
:=
s
.
GetState
(
db
,
key
)
if
prev
==
value
{
return
}
// New value is different, update and journal the change
s
.
setState
(
key
,
value
)
}
// SetStorage replaces the entire state storage with the given one.
//
// After this function is called, all original state will be ignored and state
// lookup only happens in the fake state storage.
//
// Note this function should only be used for debugging purpose.
func
(
s
*
stateObject
)
SetStorage
(
storage
map
[
common
.
Hash
]
common
.
Hash
)
{
// Allocate fake storage if it's nil.
if
s
.
fakeStorage
==
nil
{
s
.
fakeStorage
=
make
(
Storage
)
}
for
key
,
value
:=
range
storage
{
s
.
fakeStorage
[
key
]
=
value
}
// Don't bother journal since this function should only be used for
// debugging and the `fake` storage won't be committed to database.
}
func
(
s
*
stateObject
)
setState
(
key
,
value
common
.
Hash
)
{
s
.
dirtyStorage
[
key
]
=
value
}
minigeth/core/state/statedb.go
View file @
60a349f4
...
...
@@ -3,8 +3,10 @@ package state
import
(
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -17,18 +19,37 @@ var (
)
type
StateDB
struct
{
db
Database
trie
Trie
db
Database
prefetcher
*
triePrefetcher
originalRoot
common
.
Hash
// The pre-state root, before any changes were made
trie
Trie
hasher
crypto
.
KeccakState
blockNumber
*
big
.
Int
stateRoot
common
.
Hash
// This map holds 'live' objects, which will get modified while processing a state transition.
stateObjects
map
[
common
.
Address
]
*
stateObject
stateObjects
map
[
common
.
Address
]
*
stateObject
stateObjectsPending
map
[
common
.
Address
]
struct
{}
// State objects finalized but not yet written to the trie
stateObjectsDirty
map
[
common
.
Address
]
struct
{}
// State objects modified in the current execution
// Per-transaction access list
accessList
*
accessList
preimages
map
[
common
.
Hash
][]
byte
snaps
*
snapshot
.
Tree
snap
snapshot
.
Snapshot
snapDestructs
map
[
common
.
Hash
]
struct
{}
snapAccounts
map
[
common
.
Hash
][]
byte
snapStorage
map
[
common
.
Hash
]
map
[
common
.
Hash
][]
byte
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal
*
journal
validRevisions
[]
revision
nextRevisionId
int
logs
map
[
common
.
Hash
][]
*
types
.
Log
logSize
uint
...
...
@@ -37,17 +58,32 @@ type StateDB struct {
// The refund counter, also used by state transitioning.
refund
uint64
// Measurements gathered during execution for debugging purposes
AccountReads
time
.
Duration
AccountHashes
time
.
Duration
AccountUpdates
time
.
Duration
AccountCommits
time
.
Duration
StorageReads
time
.
Duration
StorageHashes
time
.
Duration
StorageUpdates
time
.
Duration
StorageCommits
time
.
Duration
SnapshotAccountReads
time
.
Duration
SnapshotStorageReads
time
.
Duration
SnapshotCommits
time
.
Duration
}
func
NewStateDB
(
header
types
.
Header
)
*
StateDB
{
return
&
StateDB
{
blockNumber
:
header
.
Number
,
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
),
stateRoot
:
header
.
Root
,
db
:
Database
{
BlockNumber
:
header
.
Number
,
StateRoot
:
header
.
Root
},
trie
:
Trie
{
BlockNumber
:
header
.
Number
,
StateRoot
:
header
.
Root
},
accessList
:
newAccessList
(),
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
),
blockNumber
:
header
.
Number
,
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
),
stateObjectsPending
:
make
(
map
[
common
.
Address
]
struct
{}),
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{}),
stateRoot
:
header
.
Root
,
db
:
Database
{
BlockNumber
:
header
.
Number
,
StateRoot
:
header
.
Root
},
trie
:
Trie
{
BlockNumber
:
header
.
Number
,
StateRoot
:
header
.
Root
},
accessList
:
newAccessList
(),
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
),
}
}
...
...
minigeth/metrics/metrics.go
0 → 100644
View file @
60a349f4
package
metrics
var
EnabledExpensive
=
false
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