aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md9
-rw-r--r--accounts/abi/abi.go32
-rw-r--r--accounts/abi/abi_test.go82
-rw-r--r--accounts/abi/argument.go5
-rw-r--r--accounts/abi/event.go44
-rw-r--r--accounts/abi/event_test.go40
-rw-r--r--accounts/abi/type.go2
-rw-r--r--cmd/gethrpctest/main.go4
-rw-r--r--core/state/statedb.go10
-rw-r--r--core/state/statedb_test.go120
-rw-r--r--core/vm/common.go2
-rw-r--r--core/vm/vm.go2
-rw-r--r--eth/api.go337
-rw-r--r--eth/filters/api.go4
-rw-r--r--ethdb/memory_database.go2
-rw-r--r--rpc/javascript.go5
-rw-r--r--rpc/server.go2
-rw-r--r--rpc/types.go13
-rw-r--r--trie/secure_trie.go53
19 files changed, 612 insertions, 156 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 918a2c154..829bf5d43 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,3 +1,12 @@
+## Can I have feature X
+
+Before you do a feature request please check and make sure that it isn't possible
+through some other means. The JavaScript enabled console is a powerful feature
+in the right hands. Please check our [Bitchin' tricks](https://github.com/ethereum/go-ethereum/wiki/bitchin-tricks) wiki page for more info
+and help.
+
+## Contributing
+
If you'd like to contribute to go-ethereum please fork, fix, commit and
send a pull request. Commits who do not comply with the coding standards
are ignored (use gofmt!). If you send pull requests make absolute sure that you
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 635dc43fe..b84fd463a 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -37,6 +37,7 @@ type Executer func(datain []byte) []byte
// packs data accordingly.
type ABI struct {
Methods map[string]Method
+ Events map[string]Event
}
// JSON returns a parsed ABI interface and error if it failed.
@@ -149,14 +150,37 @@ func (abi ABI) Call(executer Executer, name string, args ...interface{}) interfa
}
func (abi *ABI) UnmarshalJSON(data []byte) error {
- var methods []Method
- if err := json.Unmarshal(data, &methods); err != nil {
+ var fields []struct {
+ Type string
+ Name string
+ Const bool
+ Indexed bool
+ Inputs []Argument
+ Outputs []Argument
+ }
+
+ if err := json.Unmarshal(data, &fields); err != nil {
return err
}
abi.Methods = make(map[string]Method)
- for _, method := range methods {
- abi.Methods[method.Name] = method
+ abi.Events = make(map[string]Event)
+ for _, field := range fields {
+ switch field.Type {
+ // empty defaults to function according to the abi spec
+ case "function", "":
+ abi.Methods[field.Name] = Method{
+ Name: field.Name,
+ Const: field.Const,
+ Inputs: field.Inputs,
+ Outputs: field.Outputs,
+ }
+ case "event":
+ abi.Events[field.Name] = Event{
+ Name: field.Name,
+ Inputs: field.Inputs,
+ }
+ }
}
return nil
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index d514fb8c7..000c118f8 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -31,25 +31,25 @@ import (
const jsondata = `
[
- { "name" : "balance", "const" : true },
- { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
+ { "type" : "function", "name" : "balance", "const" : true },
+ { "type" : "function", "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
]`
const jsondata2 = `
[
- { "name" : "balance", "const" : true },
- { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
- { "name" : "test", "const" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
- { "name" : "string", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
- { "name" : "bool", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
- { "name" : "address", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
- { "name" : "string32", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string32" } ] },
- { "name" : "uint64[2]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
- { "name" : "uint64[]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
- { "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
- { "name" : "bar", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
- { "name" : "slice", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
- { "name" : "slice256", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }
+ { "type" : "function", "name" : "balance", "const" : true },
+ { "type" : "function", "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
+ { "type" : "function", "name" : "test", "const" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
+ { "type" : "function", "name" : "string", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
+ { "type" : "function", "name" : "bool", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
+ { "type" : "function", "name" : "address", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
+ { "type" : "function", "name" : "string32", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string32" } ] },
+ { "type" : "function", "name" : "uint64[2]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
+ { "type" : "function", "name" : "uint64[]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
+ { "type" : "function", "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
+ { "type" : "function", "name" : "bar", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
+ { "type" : "function", "name" : "slice", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
+ { "type" : "function", "name" : "slice256", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }
]`
func TestType(t *testing.T) {
@@ -96,7 +96,7 @@ func TestReader(t *testing.T) {
},
"send": Method{
"send", false, []Argument{
- Argument{"amount", Uint256},
+ Argument{"amount", Uint256, false},
}, nil,
},
},
@@ -238,7 +238,7 @@ func TestTestAddress(t *testing.T) {
func TestMethodSignature(t *testing.T) {
String, _ := NewType("string")
String32, _ := NewType("string32")
- m := Method{"foo", false, []Argument{Argument{"bar", String32}, Argument{"baz", String}}, nil}
+ m := Method{"foo", false, []Argument{Argument{"bar", String32, false}, Argument{"baz", String, false}}, nil}
exp := "foo(string32,string)"
if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig())
@@ -250,7 +250,7 @@ func TestMethodSignature(t *testing.T) {
}
uintt, _ := NewType("uint")
- m = Method{"foo", false, []Argument{Argument{"bar", uintt}}, nil}
+ m = Method{"foo", false, []Argument{Argument{"bar", uintt, false}}, nil}
exp = "foo(uint256)"
if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig())
@@ -367,8 +367,8 @@ func ExampleJSON() {
func TestBytes(t *testing.T) {
const definition = `[
- { "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] },
- { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
+ { "type" : "function", "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] },
+ { "type" : "function", "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
]`
abi, err := JSON(strings.NewReader(definition))
@@ -396,10 +396,8 @@ func TestBytes(t *testing.T) {
func TestReturn(t *testing.T) {
const definition = `[
- { "name" : "balance", "const" : true, "inputs" : [], "outputs" : [ { "name": "", "type": "hash" } ] },
- { "name" : "name", "const" : true, "inputs" : [], "outputs" : [ { "name": "", "type": "address" } ] }
-
-]`
+ { "type" : "function", "name" : "balance", "const" : true, "inputs" : [], "outputs" : [ { "name": "", "type": "hash" } ] },
+ { "type" : "function", "name" : "name", "const" : true, "inputs" : [], "outputs" : [ { "name": "", "type": "address" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
@@ -424,3 +422,39 @@ func TestReturn(t *testing.T) {
t.Errorf("expected type common.Address, got %T", r)
}
}
+
+func TestDefaultFunctionParsing(t *testing.T) {
+ const definition = `[{ "name" : "balance" }]`
+
+ abi, err := JSON(strings.NewReader(definition))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, ok := abi.Methods["balance"]; !ok {
+ t.Error("expected 'balance' to be present")
+ }
+}
+
+func TestBareEvents(t *testing.T) {
+ const definition = `[
+ { "type" : "event", "name" : "balance" },
+ { "type" : "event", "name" : "name" }]`
+
+ abi, err := JSON(strings.NewReader(definition))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(abi.Events) != 2 {
+ t.Error("expected 2 events")
+ }
+
+ if _, ok := abi.Events["balance"]; !ok {
+ t.Error("expected 'balance' event to be present")
+ }
+
+ if _, ok := abi.Events["name"]; !ok {
+ t.Error("expected 'name' event to be present")
+ }
+}
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
index 8907b2979..188203a5d 100644
--- a/accounts/abi/argument.go
+++ b/accounts/abi/argument.go
@@ -24,8 +24,9 @@ import (
// Argument holds the name of the argument and the corresponding type.
// Types are used when packing and testing arguments.
type Argument struct {
- Name string
- Type Type
+ Name string
+ Type Type
+ Indexed bool // indexed is only used by events
}
func (a *Argument) UnmarshalJSON(data []byte) error {
diff --git a/accounts/abi/event.go b/accounts/abi/event.go
new file mode 100644
index 000000000..7c4e092ea
--- /dev/null
+++ b/accounts/abi/event.go
@@ -0,0 +1,44 @@
+// 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 abi
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+// Event is an event potentially triggered by the EVM's LOG mechanism. The Event
+// holds type information (inputs) about the yielded output
+type Event struct {
+ Name string
+ Inputs []Argument
+}
+
+// Id returns the canonical representation of the event's signature used by the
+// abi definition to identify event names and types.
+func (e Event) Id() common.Hash {
+ types := make([]string, len(e.Inputs))
+ i := 0
+ for _, input := range e.Inputs {
+ types[i] = input.Type.String()
+ i++
+ }
+ return common.BytesToHash(crypto.Sha3([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
+}
diff --git a/accounts/abi/event_test.go b/accounts/abi/event_test.go
new file mode 100644
index 000000000..34a7a1684
--- /dev/null
+++ b/accounts/abi/event_test.go
@@ -0,0 +1,40 @@
+package abi
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+func TestEventId(t *testing.T) {
+ var table = []struct {
+ definition string
+ expectations map[string]common.Hash
+ }{
+ {
+ definition: `[
+ { "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint" }] },
+ { "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
+ ]`,
+ expectations: map[string]common.Hash{
+ "balance": crypto.Sha3Hash([]byte("balance(uint256)")),
+ "check": crypto.Sha3Hash([]byte("check(address,uint256)")),
+ },
+ },
+ }
+
+ for _, test := range table {
+ abi, err := JSON(strings.NewReader(test.definition))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for name, event := range abi.Events {
+ if event.Id() != test.expectations[name] {
+ t.Errorf("expected id to be %x, got %x", test.expectations[name], event.Id())
+ }
+ }
+ }
+}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 8f0238fc9..32f761ef0 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -218,5 +218,5 @@ func (t Type) pack(v interface{}) ([]byte, error) {
}
}
- return nil, fmt.Errorf("ABI: bad input given %T", value.Kind())
+ return nil, fmt.Errorf("ABI: bad input given %v", value.Kind())
}
diff --git a/cmd/gethrpctest/main.go b/cmd/gethrpctest/main.go
index b4530ca51..8522258a9 100644
--- a/cmd/gethrpctest/main.go
+++ b/cmd/gethrpctest/main.go
@@ -52,6 +52,10 @@ var (
func main() {
flag.Parse()
+ // Enable logging errors, we really do want to see those
+ glog.SetV(2)
+ glog.SetToStderr(true)
+
// Load the test suite to run the RPC against
tests, err := tests.LoadBlockTests(*testFile)
if err != nil {
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 8093472b5..22ffa36a0 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -206,9 +206,6 @@ func (self *StateDB) Delete(addr common.Address) bool {
// Update the given state object and apply it to state trie
func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
- if len(stateObject.code) > 0 {
- self.db.Put(stateObject.codeHash, stateObject.code)
- }
addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
@@ -375,8 +372,15 @@ func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
// and just mark it for deletion in the trie.
s.DeleteStateObject(stateObject)
} else {
+ // Write any contract code associated with the state object
+ if len(stateObject.code) > 0 {
+ if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {
+ return common.Hash{}, err
+ }
+ }
// Write any storage changes in the state object to its trie.
stateObject.Update()
+
// Commit the trie of the object to the batch.
// This updates the trie root internally, so
// getting the root hash of the storage trie
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
new file mode 100644
index 000000000..8138f8d78
--- /dev/null
+++ b/core/state/statedb_test.go
@@ -0,0 +1,120 @@
+// Copyright 2015 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"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+)
+
+// Tests that updating a state trie does not leak any database writes prior to
+// actually committing the state.
+func TestUpdateLeaks(t *testing.T) {
+ // Create an empty state database
+ db, _ := ethdb.NewMemDatabase()
+ state, _ := New(common.Hash{}, db)
+
+ // Update it with some accounts
+ for i := byte(0); i < 255; i++ {
+ obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
+ obj.AddBalance(big.NewInt(int64(11 * i)))
+ obj.SetNonce(uint64(42 * i))
+ if i%2 == 0 {
+ obj.SetState(common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
+ }
+ if i%3 == 0 {
+ obj.SetCode([]byte{i, i, i, i, i})
+ }
+ state.UpdateStateObject(obj)
+ }
+ // Ensure that no data was leaked into the database
+ for _, key := range db.Keys() {
+ value, _ := db.Get(key)
+ t.Errorf("State leaked into database: %x -> %x", key, value)
+ }
+}
+
+// Tests that no intermediate state of an object is stored into the database,
+// only the one right before the commit.
+func TestIntermediateLeaks(t *testing.T) {
+ // Create two state databases, one transitioning to the final state, the other final from the beginning
+ transDb, _ := ethdb.NewMemDatabase()
+ finalDb, _ := ethdb.NewMemDatabase()
+ transState, _ := New(common.Hash{}, transDb)
+ finalState, _ := New(common.Hash{}, finalDb)
+
+ // Update the states with some objects
+ for i := byte(0); i < 255; i++ {
+ // Create a new state object with some data into the transition database
+ obj := transState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
+ obj.SetBalance(big.NewInt(int64(11 * i)))
+ obj.SetNonce(uint64(42 * i))
+ if i%2 == 0 {
+ obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.BytesToHash([]byte{i, i, i, i, 0}))
+ }
+ if i%3 == 0 {
+ obj.SetCode([]byte{i, i, i, i, i, 0})
+ }
+ transState.UpdateStateObject(obj)
+
+ // Overwrite all the data with new values in the transition database
+ obj.SetBalance(big.NewInt(int64(11*i + 1)))
+ obj.SetNonce(uint64(42*i + 1))
+ if i%2 == 0 {
+ obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.Hash{})
+ obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
+ }
+ if i%3 == 0 {
+ obj.SetCode([]byte{i, i, i, i, i, 1})
+ }
+ transState.UpdateStateObject(obj)
+
+ // Create the final state object directly in the final database
+ obj = finalState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
+ obj.SetBalance(big.NewInt(int64(11*i + 1)))
+ obj.SetNonce(uint64(42*i + 1))
+ if i%2 == 0 {
+ obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
+ }
+ if i%3 == 0 {
+ obj.SetCode([]byte{i, i, i, i, i, 1})
+ }
+ finalState.UpdateStateObject(obj)
+ }
+ if _, err := transState.Commit(); err != nil {
+ t.Fatalf("failed to commit transition state: %v", err)
+ }
+ if _, err := finalState.Commit(); err != nil {
+ t.Fatalf("failed to commit final state: %v", err)
+ }
+ // Cross check the databases to ensure they are the same
+ for _, key := range finalDb.Keys() {
+ if _, err := transDb.Get(key); err != nil {
+ val, _ := finalDb.Get(key)
+ t.Errorf("entry missing from the transition database: %x -> %x", key, val)
+ }
+ }
+ for _, key := range transDb.Keys() {
+ if _, err := finalDb.Get(key); err != nil {
+ val, _ := transDb.Get(key)
+ t.Errorf("extra entry in the transition database: %x -> %x", key, val)
+ }
+ }
+}
diff --git a/core/vm/common.go b/core/vm/common.go
index 2d1aa9332..395ed0471 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -28,6 +28,8 @@ import (
// Global Debug flag indicating Debug VM (full logging)
var Debug bool
+var GenerateStructLogs bool = false
+
// Type is the VM type accepted by **NewVm**
type Type byte
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 8e07aaa89..0c6bbcd42 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -367,7 +367,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Co
// log emits a log event to the environment for each opcode encountered. This is not to be confused with the
// LOG* opcode.
func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) {
- if Debug {
+ if Debug || GenerateStructLogs {
mem := make([]byte, len(memory.Data()))
copy(mem, memory.Data())
diff --git a/eth/api.go b/eth/api.go
index 0f55c60a9..0fa855f15 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -53,19 +53,40 @@ const (
defaultGas = uint64(90000)
)
-// blockByNumber is a commonly used helper function which retrieves and returns the block for the given block number. It
-// returns nil when no block could be found.
+// blockByNumber is a commonly used helper function which retrieves and returns
+// the block for the given block number, capable of handling two special blocks:
+// rpc.LatestBlockNumber adn rpc.PendingBlockNumber. It returns nil when no block
+// could be found.
func blockByNumber(m *miner.Miner, bc *core.BlockChain, blockNr rpc.BlockNumber) *types.Block {
+ // Pending block is only known by the miner
if blockNr == rpc.PendingBlockNumber {
return m.PendingBlock()
}
+ // Otherwise resolve and return the block
if blockNr == rpc.LatestBlockNumber {
return bc.CurrentBlock()
}
-
return bc.GetBlockByNumber(uint64(blockNr))
}
+// stateAndBlockByNumber is a commonly used helper function which retrieves and
+// returns the state and containing block for the given block number, capable of
+// handling two special states: rpc.LatestBlockNumber adn rpc.PendingBlockNumber.
+// It returns nil when no block or state could be found.
+func stateAndBlockByNumber(m *miner.Miner, bc *core.BlockChain, blockNr rpc.BlockNumber, chainDb ethdb.Database) (*state.StateDB, *types.Block, error) {
+ // Pending state is only known by the miner
+ if blockNr == rpc.PendingBlockNumber {
+ return m.PendingState(), m.PendingBlock(), nil
+ }
+ // Otherwise resolve the block number and return its state
+ block := blockByNumber(m, bc, blockNr)
+ if block == nil {
+ return nil, nil, nil
+ }
+ stateDb, err := state.New(block.Root(), chainDb)
+ return stateDb, block, err
+}
+
// PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct {
@@ -395,16 +416,12 @@ func (s *PublicBlockChainAPI) BlockNumber() *big.Int {
return s.bc.CurrentHeader().Number
}
-// GetBalance returns the amount of wei for the given address in the state of the given block number.
-// When block number equals rpc.LatestBlockNumber the current block is used.
+// GetBalance returns the amount of wei for the given address in the state of the
+// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
+// block numbers are also allowed.
func (s *PublicBlockChainAPI) GetBalance(address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) {
- block := blockByNumber(s.miner, s.bc, blockNr)
- if block == nil {
- return nil, nil
- }
-
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
return nil, err
}
return state.GetBalance(address), nil
@@ -414,7 +431,14 @@ func (s *PublicBlockChainAPI) GetBalance(address common.Address, blockNr rpc.Blo
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByNumber(blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
- return s.rpcOutputBlock(block, true, fullTx)
+ response, err := s.rpcOutputBlock(block, true, fullTx)
+ if err == nil && blockNr == rpc.PendingBlockNumber {
+ // Pending blocks need to nil out a few fields
+ for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} {
+ response[field] = nil
+ }
+ }
+ return response, err
}
return nil, nil
}
@@ -431,10 +455,6 @@ func (s *PublicBlockChainAPI) GetBlockByHash(blockHash common.Hash, fullTx bool)
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(blockNr rpc.BlockNumber, index rpc.HexNumber) (map[string]interface{}, error) {
- if blockNr == rpc.PendingBlockNumber {
- return nil, nil
- }
-
if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
uncles := block.Uncles()
if index.Int() < 0 || index.Int() >= len(uncles) {
@@ -464,10 +484,6 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(blockHash common.Hash,
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(blockNr rpc.BlockNumber) *rpc.HexNumber {
- if blockNr == rpc.PendingBlockNumber {
- return rpc.NewHexNumber(0)
- }
-
if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
return rpc.NewHexNumber(len(block.Uncles()))
}
@@ -508,38 +524,26 @@ func (s *PublicBlockChainAPI) NewBlocks(args NewBlocksArgs) (rpc.Subscription, e
// GetCode returns the code stored at the given address in the state for the given block number.
func (s *PublicBlockChainAPI) GetCode(address common.Address, blockNr rpc.BlockNumber) (string, error) {
- return s.GetData(address, blockNr)
-}
-
-// GetData returns the data stored at the given address in the state for the given block number.
-func (s *PublicBlockChainAPI) GetData(address common.Address, blockNr rpc.BlockNumber) (string, error) {
- if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
- return "", err
- }
- res := state.GetCode(address)
- if len(res) == 0 { // backwards compatibility
- return "0x", nil
- }
- return common.ToHex(res), nil
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
+ return "", err
}
-
- return "0x", nil
+ res := state.GetCode(address)
+ if len(res) == 0 { // backwards compatibility
+ return "0x", nil
+ }
+ return common.ToHex(res), nil
}
-// GetStorageAt returns the storage from the state at the given address, key and block number.
+// GetStorageAt returns the storage from the state at the given address, key and
+// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
+// numbers are also allowed.
func (s *PublicBlockChainAPI) GetStorageAt(address common.Address, key string, blockNr rpc.BlockNumber) (string, error) {
- if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
- return "", err
- }
-
- return state.GetState(address, common.HexToHash(key)).Hex(), nil
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
+ return "0x", err
}
-
- return "0x", nil
+ return state.GetState(address, common.HexToHash(key)).Hex(), nil
}
// callmsg is the message type used for call transations.
@@ -570,55 +574,51 @@ type CallArgs struct {
}
func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) {
- if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
- stateDb, err := state.New(block.Root(), s.chainDb)
- if err != nil {
- return "0x", nil, err
- }
-
- stateDb = stateDb.Copy()
- var from *state.StateObject
- if args.From == (common.Address{}) {
- accounts, err := s.am.Accounts()
- if err != nil || len(accounts) == 0 {
- from = stateDb.GetOrNewStateObject(common.Address{})
- } else {
- from = stateDb.GetOrNewStateObject(accounts[0].Address)
- }
+ // Fetch the state associated with the block number
+ stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if stateDb == nil || err != nil {
+ return "0x", nil, err
+ }
+ stateDb = stateDb.Copy()
+
+ // Retrieve the account state object to interact with
+ var from *state.StateObject
+ if args.From == (common.Address{}) {
+ accounts, err := s.am.Accounts()
+ if err != nil || len(accounts) == 0 {
+ from = stateDb.GetOrNewStateObject(common.Address{})
} else {
- from = stateDb.GetOrNewStateObject(args.From)
- }
-
- from.SetBalance(common.MaxBig)
-
- msg := callmsg{
- from: from,
- to: &args.To,
- gas: args.Gas.BigInt(),
- gasPrice: args.GasPrice.BigInt(),
- value: args.Value.BigInt(),
- data: common.FromHex(args.Data),
- }
-
- if msg.gas.Cmp(common.Big0) == 0 {
- msg.gas = big.NewInt(50000000)
- }
-
- if msg.gasPrice.Cmp(common.Big0) == 0 {
- msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
+ from = stateDb.GetOrNewStateObject(accounts[0].Address)
}
+ } else {
+ from = stateDb.GetOrNewStateObject(args.From)
+ }
+ from.SetBalance(common.MaxBig)
- header := s.bc.CurrentBlock().Header()
- vmenv := core.NewEnv(stateDb, s.bc, msg, header)
- gp := new(core.GasPool).AddGas(common.MaxBig)
- res, gas, err := core.ApplyMessage(vmenv, msg, gp)
- if len(res) == 0 { // backwards compatability
- return "0x", gas, err
- }
- return common.ToHex(res), gas, err
+ // Assemble the CALL invocation
+ msg := callmsg{
+ from: from,
+ to: &args.To,
+ gas: args.Gas.BigInt(),
+ gasPrice: args.GasPrice.BigInt(),
+ value: args.Value.BigInt(),
+ data: common.FromHex(args.Data),
+ }
+ if msg.gas.Cmp(common.Big0) == 0 {
+ msg.gas = big.NewInt(50000000)
}
+ if msg.gasPrice.Cmp(common.Big0) == 0 {
+ msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
+ }
+ // Execute the call and return
+ vmenv := core.NewEnv(stateDb, s.bc, msg, block.Header())
+ gp := new(core.GasPool).AddGas(common.MaxBig)
- return "0x", common.Big0, nil
+ res, gas, err := core.ApplyMessage(vmenv, msg, gp)
+ if len(res) == 0 { // backwards compatibility
+ return "0x", gas, err
+ }
+ return common.ToHex(res), gas, err
}
// Call executes the given transaction on the state for the given block number.
@@ -802,14 +802,9 @@ func getTransaction(chainDb ethdb.Database, txPool *core.TxPool, txHash common.H
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(blockNr rpc.BlockNumber) *rpc.HexNumber {
- if blockNr == rpc.PendingBlockNumber {
- return rpc.NewHexNumber(0)
- }
-
if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
return rpc.NewHexNumber(len(block.Transactions()))
}
-
return nil
}
@@ -839,13 +834,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(blockHash c
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
func (s *PublicTransactionPoolAPI) GetTransactionCount(address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) {
- block := blockByNumber(s.miner, s.bc, blockNr)
- if block == nil {
- return nil, nil
- }
-
- state, err := state.New(block.Root(), s.chainDb)
- if err != nil {
+ state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if state == nil || err != nil {
return nil, err
}
return rpc.NewHexNumber(state.GetNonce(address)), nil
@@ -1501,6 +1491,145 @@ func (api *PrivateDebugAPI) SetHead(number uint64) {
api.eth.BlockChain().SetHead(number)
}
+// StructLogRes stores a structured log emitted by the evm while replaying a
+// transaction in debug mode
+type structLogRes struct {
+ Pc uint64 `json:"pc"`
+ Op string `json:"op"`
+ Gas *big.Int `json:"gas"`
+ GasCost *big.Int `json:"gasCost"`
+ Error error `json:"error"`
+ Stack []string `json:"stack"`
+ Memory map[string]string `json:"memory"`
+ Storage map[string]string `json:"storage"`
+}
+
+// TransactionExecutionRes groups all structured logs emitted by the evm
+// while replaying a transaction in debug mode as well as the amount of
+// gas used and the return value
+type TransactionExecutionResult struct {
+ Gas *big.Int `json:"gas"`
+ ReturnValue string `json:"returnValue"`
+ StructLogs []structLogRes `json:"structLogs"`
+}
+
+func (s *PrivateDebugAPI) doReplayTransaction(txHash common.Hash) ([]vm.StructLog, []byte, *big.Int, error) {
+ // Retrieve the tx from the chain
+ tx, _, blockIndex, _ := core.GetTransaction(s.eth.ChainDb(), txHash)
+
+ if tx == nil {
+ return nil, nil, nil, fmt.Errorf("Transaction not found")
+ }
+
+ block := s.eth.BlockChain().GetBlockByNumber(blockIndex - 1)
+ if block == nil {
+ return nil, nil, nil, fmt.Errorf("Unable to retrieve prior block")
+ }
+
+ // Create the state database
+ stateDb, err := state.New(block.Root(), s.eth.ChainDb())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ txFrom, err := tx.From()
+
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("Unable to create transaction sender")
+ }
+ from := stateDb.GetOrNewStateObject(txFrom)
+ msg := callmsg{
+ from: from,
+ to: tx.To(),
+ gas: tx.Gas(),
+ gasPrice: tx.GasPrice(),
+ value: tx.Value(),
+ data: tx.Data(),
+ }
+
+ vmenv := core.NewEnv(stateDb, s.eth.BlockChain(), msg, block.Header())
+ gp := new(core.GasPool).AddGas(block.GasLimit())
+ vm.GenerateStructLogs = true
+ defer func() { vm.GenerateStructLogs = false }()
+
+ ret, gas, err := core.ApplyMessage(vmenv, msg, gp)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("Error executing transaction %v", err)
+ }
+
+ return vmenv.StructLogs(), ret, gas, nil
+}
+
+// Executes a transaction and returns the structured logs of the evm
+// gathered during the execution
+func (s *PrivateDebugAPI) ReplayTransaction(txHash common.Hash, stackDepth int, memorySize int, storageSize int) (*TransactionExecutionResult, error) {
+
+ structLogs, ret, gas, err := s.doReplayTransaction(txHash)
+
+ if err != nil {
+ return nil, err
+ }
+
+ res := TransactionExecutionResult{
+ Gas: gas,
+ ReturnValue: fmt.Sprintf("%x", ret),
+ StructLogs: make([]structLogRes, len(structLogs)),
+ }
+
+ for index, trace := range structLogs {
+
+ stackLength := len(trace.Stack)
+
+ // Return full stack by default
+ if stackDepth != -1 && stackDepth < stackLength {
+ stackLength = stackDepth
+ }
+
+ res.StructLogs[index] = structLogRes{
+ Pc: trace.Pc,
+ Op: trace.Op.String(),
+ Gas: trace.Gas,
+ GasCost: trace.GasCost,
+ Error: trace.Err,
+ Stack: make([]string, stackLength),
+ Memory: make(map[string]string),
+ Storage: make(map[string]string),
+ }
+
+ for i := 0; i < stackLength; i++ {
+ res.StructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(trace.Stack[i].Bytes(), 32))
+ }
+
+ addr := 0
+ memorySizeLocal := memorySize
+
+ // Return full memory by default
+ if memorySize == -1 {
+ memorySizeLocal = len(trace.Memory)
+ }
+
+ for i := 0; i+16 <= len(trace.Memory) && addr < memorySizeLocal; i += 16 {
+ res.StructLogs[index].Memory[fmt.Sprintf("%04d", addr*16)] = fmt.Sprintf("%x", trace.Memory[i:i+16])
+ addr++
+ }
+
+ storageLength := len(trace.Stack)
+ if storageSize != -1 && storageSize < storageLength {
+ storageLength = storageSize
+ }
+
+ i := 0
+ for storageIndex, storageValue := range trace.Storage {
+ if i >= storageLength {
+ break
+ }
+ res.StructLogs[index].Storage[fmt.Sprintf("%x", storageIndex)] = fmt.Sprintf("%x", storageValue)
+ i++
+ }
+ }
+ return &res, nil
+}
+
// PublicNetAPI offers network related RPC methods
type PublicNetAPI struct {
net *p2p.Server
diff --git a/eth/filters/api.go b/eth/filters/api.go
index f2b0ed32f..aa4c305a6 100644
--- a/eth/filters/api.go
+++ b/eth/filters/api.go
@@ -239,13 +239,13 @@ func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
return err
}
- if raw.From == nil {
+ if raw.From == nil || raw.From.Int64() < 0 {
args.FromBlock = rpc.LatestBlockNumber
} else {
args.FromBlock = *raw.From
}
- if raw.ToBlock == nil {
+ if raw.ToBlock == nil || raw.ToBlock.Int64() < 0 {
args.ToBlock = rpc.LatestBlockNumber
} else {
args.ToBlock = *raw.ToBlock
diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go
index 45423ed73..a729f5233 100644
--- a/ethdb/memory_database.go
+++ b/ethdb/memory_database.go
@@ -107,7 +107,7 @@ func (b *memBatch) Put(key, value []byte) error {
b.lock.Lock()
defer b.lock.Unlock()
- b.writes = append(b.writes, kv{key, common.CopyBytes(value)})
+ b.writes = append(b.writes, kv{common.CopyBytes(key), common.CopyBytes(value)})
return nil
}
diff --git a/rpc/javascript.go b/rpc/javascript.go
index c145163e5..4c0ac5354 100644
--- a/rpc/javascript.go
+++ b/rpc/javascript.go
@@ -407,6 +407,11 @@ web3._extend({
call: 'debug_writeMemProfile',
params: 1
}),
+ new web3._extend.Method({
+ name: 'replayTransaction',
+ call: 'debug_replayTransaction',
+ params: 4
+ })
],
properties:
[
diff --git a/rpc/server.go b/rpc/server.go
index 0b93a4e64..5b88d843a 100644
--- a/rpc/server.go
+++ b/rpc/server.go
@@ -332,7 +332,6 @@ func (s *Server) handle(ctx context.Context, codec ServerCodec, req *serverReque
return res
}
}
-
return codec.CreateResponse(req.id, reply[0].Interface())
}
@@ -344,7 +343,6 @@ func (s *Server) exec(ctx context.Context, codec ServerCodec, req *serverRequest
} else {
response = s.handle(ctx, codec, req)
}
-
if err := codec.Write(response); err != nil {
glog.V(logger.Error).Infof("%v\n", err)
codec.Close()
diff --git a/rpc/types.go b/rpc/types.go
index 02295a022..f268d84db 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -174,12 +174,14 @@ type HexNumber big.Int
// NewHexNumber creates a new hex number instance which will serialize the given val with `%#x` on marshal.
func NewHexNumber(val interface{}) *HexNumber {
if val == nil {
- return nil
+ return nil // note, this doesn't catch nil pointers, only passing nil directly!
}
- if v, ok := val.(*big.Int); ok && v != nil {
- hn := new(big.Int).Set(v)
- return (*HexNumber)(hn)
+ if v, ok := val.(*big.Int); ok {
+ if v != nil {
+ return (*HexNumber)(new(big.Int).Set(v))
+ }
+ return nil
}
rval := reflect.ValueOf(val)
@@ -303,10 +305,9 @@ const (
)
// UnmarshalJSON parses the given JSON fragement into a BlockNumber. It supports:
-// - "latest" or "earliest" as string arguments
+// - "latest", "earliest" or "pending" as string arguments
// - the block number
// Returned errors:
-// - an unsupported error when "pending" is specified (not yet implemented)
// - an invalid block number error when the given argument isn't a known strings
// - an out of range error when the given block number is either too little or too large
func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
diff --git a/trie/secure_trie.go b/trie/secure_trie.go
index caeef3c3a..be7defe83 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -40,9 +40,10 @@ var secureKeyPrefix = []byte("secure-key-")
type SecureTrie struct {
*Trie
- hash hash.Hash
- secKeyBuf []byte
- hashKeyBuf []byte
+ hash hash.Hash
+ hashKeyBuf []byte
+ secKeyBuf []byte
+ secKeyCache map[string][]byte
}
// NewSecure creates a trie with an existing root node from db.
@@ -59,7 +60,10 @@ func NewSecure(root common.Hash, db Database) (*SecureTrie, error) {
if err != nil {
return nil, err
}
- return &SecureTrie{Trie: trie}, nil
+ return &SecureTrie{
+ Trie: trie,
+ secKeyCache: make(map[string][]byte),
+ }, nil
}
// Get returns the value for key stored in the trie.
@@ -105,7 +109,7 @@ func (t *SecureTrie) TryUpdate(key, value []byte) error {
if err != nil {
return err
}
- t.Trie.db.Put(t.secKey(hk), key)
+ t.secKeyCache[string(hk)] = common.CopyBytes(key)
return nil
}
@@ -119,16 +123,53 @@ func (t *SecureTrie) Delete(key []byte) {
// TryDelete removes any existing value for key from the trie.
// If a node was not found in the database, a MissingNodeError is returned.
func (t *SecureTrie) TryDelete(key []byte) error {
- return t.Trie.TryDelete(t.hashKey(key))
+ hk := t.hashKey(key)
+ delete(t.secKeyCache, string(hk))
+ return t.Trie.TryDelete(hk)
}
// GetKey returns the sha3 preimage of a hashed key that was
// previously used to store a value.
func (t *SecureTrie) GetKey(shaKey []byte) []byte {
+ if key, ok := t.secKeyCache[string(shaKey)]; ok {
+ return key
+ }
key, _ := t.Trie.db.Get(t.secKey(shaKey))
return key
}
+// Commit writes all nodes and the secure hash pre-images to the trie's database.
+// Nodes are stored with their sha3 hash as the key.
+//
+// Committing flushes nodes from memory. Subsequent Get calls will load nodes
+// from the database.
+func (t *SecureTrie) Commit() (root common.Hash, err error) {
+ return t.CommitTo(t.db)
+}
+
+// CommitTo writes all nodes and the secure hash pre-images to the given database.
+// Nodes are stored with their sha3 hash as the key.
+//
+// Committing flushes nodes from memory. Subsequent Get calls will load nodes from
+// the trie's database. Calling code must ensure that the changes made to db are
+// written back to the trie's attached database before using the trie.
+func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) {
+ if len(t.secKeyCache) > 0 {
+ for hk, key := range t.secKeyCache {
+ if err := db.Put(t.secKey([]byte(hk)), key); err != nil {
+ return common.Hash{}, err
+ }
+ }
+ t.secKeyCache = make(map[string][]byte)
+ }
+ n, err := t.hashRoot(db)
+ if err != nil {
+ return (common.Hash{}), err
+ }
+ t.root = n
+ return common.BytesToHash(n.(hashNode)), nil
+}
+
func (t *SecureTrie) secKey(key []byte) []byte {
t.secKeyBuf = append(t.secKeyBuf[:0], secureKeyPrefix...)
t.secKeyBuf = append(t.secKeyBuf, key...)