Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
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
张蕾
Geth-Modification
Commits
207bd7d2
Commit
207bd7d2
authored
Apr 19, 2017
by
Felix Lange
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
eth: add debug_storageRangeAt
parent
4047ccad
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
201 additions
and
39 deletions
+201
-39
state_object.go
core/state/state_object.go
+7
-2
statedb.go
core/state/statedb.go
+11
-0
api.go
eth/api.go
+90
-37
api_test.go
eth/api_test.go
+88
-0
web3ext.go
internal/web3ext/web3ext.go
+5
-0
No files found.
core/state/state_object.go
View file @
207bd7d2
...
...
@@ -201,7 +201,7 @@ func (self *stateObject) setState(key, value common.Hash) {
}
// updateTrie writes cached storage modifications into the object's storage trie.
func
(
self
*
stateObject
)
updateTrie
(
db
trie
.
Database
)
{
func
(
self
*
stateObject
)
updateTrie
(
db
trie
.
Database
)
*
trie
.
SecureTrie
{
tr
:=
self
.
getTrie
(
db
)
for
key
,
value
:=
range
self
.
dirtyStorage
{
delete
(
self
.
dirtyStorage
,
key
)
...
...
@@ -213,6 +213,7 @@ func (self *stateObject) updateTrie(db trie.Database) {
v
,
_
:=
rlp
.
EncodeToBytes
(
bytes
.
TrimLeft
(
value
[
:
],
"
\x00
"
))
tr
.
Update
(
key
[
:
],
v
)
}
return
tr
}
// UpdateRoot sets the trie root to the current root hash of
...
...
@@ -280,7 +281,11 @@ func (c *stateObject) ReturnGas(gas *big.Int) {}
func
(
self
*
stateObject
)
deepCopy
(
db
*
StateDB
,
onDirty
func
(
addr
common
.
Address
))
*
stateObject
{
stateObject
:=
newObject
(
db
,
self
.
address
,
self
.
data
,
onDirty
)
stateObject
.
trie
=
self
.
trie
if
self
.
trie
!=
nil
{
// A shallow copy makes the two tries independent.
cpy
:=
*
self
.
trie
stateObject
.
trie
=
&
cpy
}
stateObject
.
code
=
self
.
code
stateObject
.
dirtyStorage
=
self
.
dirtyStorage
.
Copy
()
stateObject
.
cachedStorage
=
self
.
dirtyStorage
.
Copy
()
...
...
core/state/statedb.go
View file @
207bd7d2
...
...
@@ -296,6 +296,17 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
return
common
.
Hash
{}
}
// StorageTrie returns the storage trie of an account.
// The return value is a copy and is nil for non-existent accounts.
func
(
self
*
StateDB
)
StorageTrie
(
a
common
.
Address
)
*
trie
.
SecureTrie
{
stateObject
:=
self
.
getStateObject
(
a
)
if
stateObject
==
nil
{
return
nil
}
cpy
:=
stateObject
.
deepCopy
(
self
,
nil
)
return
cpy
.
updateTrie
(
self
.
db
)
}
func
(
self
*
StateDB
)
HasSuicided
(
addr
common
.
Address
)
bool
{
stateObject
:=
self
.
getStateObject
(
addr
)
if
stateObject
!=
nil
{
...
...
eth/api.go
View file @
207bd7d2
...
...
@@ -20,7 +20,6 @@ import (
"bytes"
"compress/gzip"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
...
...
@@ -41,6 +40,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
)
const
defaultTraceTimeout
=
5
*
time
.
Second
...
...
@@ -526,47 +526,17 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
if
tx
==
nil
{
return
nil
,
fmt
.
Errorf
(
"transaction %x not found"
,
txHash
)
}
block
:=
api
.
eth
.
BlockChain
()
.
GetBlockByHash
(
blockHash
)
if
block
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block %x not found"
,
blockHash
)
}
// Create the state database to mutate and eventually trace
parent
:=
api
.
eth
.
BlockChain
()
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
if
parent
==
nil
{
return
nil
,
fmt
.
Errorf
(
"block parent %x not found"
,
block
.
ParentHash
())
}
stateDb
,
err
:=
api
.
eth
.
BlockChain
()
.
StateAt
(
parent
.
Root
())
msg
,
context
,
statedb
,
err
:=
api
.
computeTxEnv
(
blockHash
,
int
(
txIndex
))
if
err
!=
nil
{
return
nil
,
err
}
signer
:=
types
.
MakeSigner
(
api
.
config
,
block
.
Number
())
// Mutate the state and trace the selected transaction
for
idx
,
tx
:=
range
block
.
Transactions
()
{
// Assemble the transaction call message
msg
,
err
:=
tx
.
AsMessage
(
signer
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"sender retrieval failed: %v"
,
err
)
}
context
:=
core
.
NewEVMContext
(
msg
,
block
.
Header
(),
api
.
eth
.
BlockChain
(),
nil
)
// Mutate the state if we haven't reached the tracing transaction yet
if
uint64
(
idx
)
<
txIndex
{
vmenv
:=
vm
.
NewEVM
(
context
,
stateDb
,
api
.
config
,
vm
.
Config
{})
_
,
_
,
err
:=
core
.
ApplyMessage
(
vmenv
,
msg
,
new
(
core
.
GasPool
)
.
AddGas
(
tx
.
Gas
()))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"mutation failed: %v"
,
err
)
}
stateDb
.
DeleteSuicides
()
continue
}
vmenv
:=
vm
.
NewEVM
(
context
,
stateDb
,
api
.
config
,
vm
.
Config
{
Debug
:
true
,
Tracer
:
tracer
})
// Run the transaction with tracing enabled.
vmenv
:=
vm
.
NewEVM
(
context
,
statedb
,
api
.
config
,
vm
.
Config
{
Debug
:
true
,
Tracer
:
tracer
})
ret
,
gas
,
err
:=
core
.
ApplyMessage
(
vmenv
,
msg
,
new
(
core
.
GasPool
)
.
AddGas
(
tx
.
Gas
()))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"tracing failed: %v"
,
err
)
}
switch
tracer
:=
tracer
.
(
type
)
{
case
*
vm
.
StructLogger
:
return
&
ethapi
.
ExecutionResult
{
...
...
@@ -576,9 +546,47 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
},
nil
case
*
ethapi
.
JavascriptTracer
:
return
tracer
.
GetResult
()
default
:
panic
(
fmt
.
Sprintf
(
"bad tracer type %T"
,
tracer
))
}
}
// computeTxEnv returns the execution environment of a certain transaction.
func
(
api
*
PrivateDebugAPI
)
computeTxEnv
(
blockHash
common
.
Hash
,
txIndex
int
)
(
core
.
Message
,
vm
.
Context
,
*
state
.
StateDB
,
error
)
{
// Create the parent state.
block
:=
api
.
eth
.
BlockChain
()
.
GetBlockByHash
(
blockHash
)
if
block
==
nil
{
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"block %x not found"
,
blockHash
)
}
parent
:=
api
.
eth
.
BlockChain
()
.
GetBlock
(
block
.
ParentHash
(),
block
.
NumberU64
()
-
1
)
if
parent
==
nil
{
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"block parent %x not found"
,
block
.
ParentHash
())
}
statedb
,
err
:=
api
.
eth
.
BlockChain
()
.
StateAt
(
parent
.
Root
())
if
err
!=
nil
{
return
nil
,
vm
.
Context
{},
nil
,
err
}
txs
:=
block
.
Transactions
()
// Recompute transactions up to the target index.
signer
:=
types
.
MakeSigner
(
api
.
config
,
block
.
Number
())
for
idx
,
tx
:=
range
txs
{
// Assemble the transaction call message
msg
,
_
:=
tx
.
AsMessage
(
signer
)
context
:=
core
.
NewEVMContext
(
msg
,
block
.
Header
(),
api
.
eth
.
BlockChain
(),
nil
)
if
idx
==
txIndex
{
return
msg
,
context
,
statedb
,
nil
}
return
nil
,
errors
.
New
(
"database inconsistency"
)
vmenv
:=
vm
.
NewEVM
(
context
,
statedb
,
api
.
config
,
vm
.
Config
{})
gp
:=
new
(
core
.
GasPool
)
.
AddGas
(
tx
.
Gas
())
_
,
_
,
err
:=
core
.
ApplyMessage
(
vmenv
,
msg
,
gp
)
if
err
!=
nil
{
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"tx %x failed: %v"
,
tx
.
Hash
(),
err
)
}
statedb
.
DeleteSuicides
()
}
return
nil
,
vm
.
Context
{},
nil
,
fmt
.
Errorf
(
"tx index %d out of range for block %x"
,
txIndex
,
blockHash
)
}
// Preimage is a debug API function that returns the preimage for a sha3 hash, if known.
...
...
@@ -592,3 +600,48 @@ func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hex
func
(
api
*
PrivateDebugAPI
)
GetBadBlocks
(
ctx
context
.
Context
)
([]
core
.
BadBlockArgs
,
error
)
{
return
api
.
eth
.
BlockChain
()
.
BadBlocks
()
}
// StorageRangeResult is the result of a debug_storageRangeAt API call.
type
StorageRangeResult
struct
{
Storage
storageMap
`json:"storage"`
NextKey
*
common
.
Hash
`json:"nextKey"`
// nil if Storage includes the last key in the trie.
}
type
storageMap
map
[
common
.
Hash
]
storageEntry
type
storageEntry
struct
{
Key
*
common
.
Hash
`json:"key"`
Value
common
.
Hash
`json:"value"`
}
// StorageRangeAt returns the storage at the given block height and transaction index.
func
(
api
*
PrivateDebugAPI
)
StorageRangeAt
(
ctx
context
.
Context
,
blockHash
common
.
Hash
,
txIndex
int
,
contractAddress
common
.
Address
,
keyStart
hexutil
.
Bytes
,
maxResult
int
)
(
StorageRangeResult
,
error
)
{
_
,
_
,
statedb
,
err
:=
api
.
computeTxEnv
(
blockHash
,
txIndex
)
if
err
!=
nil
{
return
StorageRangeResult
{},
err
}
st
:=
statedb
.
StorageTrie
(
contractAddress
)
if
st
==
nil
{
return
StorageRangeResult
{},
fmt
.
Errorf
(
"account %x doesn't exist"
,
contractAddress
)
}
return
storageRangeAt
(
st
,
keyStart
,
maxResult
),
nil
}
func
storageRangeAt
(
st
*
trie
.
SecureTrie
,
start
[]
byte
,
maxResult
int
)
StorageRangeResult
{
it
:=
trie
.
NewIterator
(
st
.
NodeIterator
(
start
))
result
:=
StorageRangeResult
{
Storage
:
storageMap
{}}
for
i
:=
0
;
i
<
maxResult
&&
it
.
Next
();
i
++
{
e
:=
storageEntry
{
Value
:
common
.
BytesToHash
(
it
.
Value
)}
if
preimage
:=
st
.
GetKey
(
it
.
Key
);
preimage
!=
nil
{
preimage
:=
common
.
BytesToHash
(
preimage
)
e
.
Key
=
&
preimage
}
result
.
Storage
[
common
.
BytesToHash
(
it
.
Key
)]
=
e
}
// Add the 'next key' so clients can continue downloading.
if
it
.
Next
()
{
next
:=
common
.
BytesToHash
(
it
.
Key
)
result
.
NextKey
=
&
next
}
return
result
}
eth/api_test.go
0 → 100644
View file @
207bd7d2
// 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
eth
import
(
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/ethdb"
)
var
dumper
=
spew
.
ConfigState
{
Indent
:
" "
}
func
TestStorageRangeAt
(
t
*
testing
.
T
)
{
// Create a state where account 0x010000... has a few storage entries.
var
(
db
,
_
=
ethdb
.
NewMemDatabase
()
state
,
_
=
state
.
New
(
common
.
Hash
{},
db
)
addr
=
common
.
Address
{
0x01
}
keys
=
[]
common
.
Hash
{
// hashes of Keys of storage
common
.
HexToHash
(
"340dd630ad21bf010b4e676dbfa9ba9a02175262d1fa356232cfde6cb5b47ef2"
),
common
.
HexToHash
(
"426fcb404ab2d5d8e61a3d918108006bbb0a9be65e92235bb10eefbdb6dcd053"
),
common
.
HexToHash
(
"48078cfed56339ea54962e72c37c7f588fc4f8e5bc173827ba75cb10a63a96a5"
),
common
.
HexToHash
(
"5723d2c3a83af9b735e3b7f21531e5623d183a9095a56604ead41f3582fdfb75"
),
}
storage
=
storageMap
{
keys
[
0
]
:
{
Key
:
&
common
.
Hash
{
0x02
},
Value
:
common
.
Hash
{
0x01
}},
keys
[
1
]
:
{
Key
:
&
common
.
Hash
{
0x04
},
Value
:
common
.
Hash
{
0x02
}},
keys
[
2
]
:
{
Key
:
&
common
.
Hash
{
0x01
},
Value
:
common
.
Hash
{
0x03
}},
keys
[
3
]
:
{
Key
:
&
common
.
Hash
{
0x03
},
Value
:
common
.
Hash
{
0x04
}},
}
)
for
_
,
entry
:=
range
storage
{
state
.
SetState
(
addr
,
*
entry
.
Key
,
entry
.
Value
)
}
// Check a few combinations of limit and start/end.
tests
:=
[]
struct
{
start
[]
byte
limit
int
want
StorageRangeResult
}{
{
start
:
[]
byte
{},
limit
:
0
,
want
:
StorageRangeResult
{
storageMap
{},
&
keys
[
0
]},
},
{
start
:
[]
byte
{},
limit
:
100
,
want
:
StorageRangeResult
{
storage
,
nil
},
},
{
start
:
[]
byte
{},
limit
:
2
,
want
:
StorageRangeResult
{
storageMap
{
keys
[
0
]
:
storage
[
keys
[
0
]],
keys
[
1
]
:
storage
[
keys
[
1
]]},
&
keys
[
2
]},
},
{
start
:
[]
byte
{
0x00
},
limit
:
4
,
want
:
StorageRangeResult
{
storage
,
nil
},
},
{
start
:
[]
byte
{
0x40
},
limit
:
2
,
want
:
StorageRangeResult
{
storageMap
{
keys
[
1
]
:
storage
[
keys
[
1
]],
keys
[
2
]
:
storage
[
keys
[
2
]]},
&
keys
[
3
]},
},
}
for
_
,
test
:=
range
tests
{
result
:=
storageRangeAt
(
state
.
StorageTrie
(
addr
),
test
.
start
,
test
.
limit
)
if
!
reflect
.
DeepEqual
(
result
,
test
.
want
)
{
t
.
Fatalf
(
"wrong result for range 0x%x.., limit %d:
\n
got %s
\n
want %s"
,
test
.
start
,
test
.
limit
,
dumper
.
Sdump
(
result
),
dumper
.
Sdump
(
&
test
.
want
))
}
}
}
internal/web3ext/web3ext.go
View file @
207bd7d2
...
...
@@ -345,6 +345,11 @@ web3._extend({
call: 'debug_getBadBlocks',
params: 0,
}),
new web3._extend.Method({
name: 'storageRangeAt',
call: 'debug_storageRangeAt',
params: 5,
}),
],
properties: []
});
...
...
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