From 4a231cd951faaf011cdec8fdde66a948b637718e Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 7 Feb 2020 10:44:32 +0100 Subject: [PATCH 1/8] internal/ethapi: return non-null "number" for pending block (#20616) Fixes: #20587, ethereum/web3.py#1572 --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 37433f335a..bec62c1290 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -642,7 +642,7 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B response, err := s.rpcMarshalBlock(block, true, fullTx) if err == nil && number == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields - for _, field := range []string{"hash", "nonce", "miner", "number"} { + for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil } } From 675f4e75b868289993fa2cb9eb543532a3c80610 Mon Sep 17 00:00:00 2001 From: Nick Ward Date: Sun, 9 Feb 2020 20:18:47 +0400 Subject: [PATCH 2/8] README.md: update evm usage example (#20635) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 757945e914..72479b5a1e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ directory. | **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options. | | `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. | | `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | -| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). | +| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | | `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. | | `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | | `puppeth` | a CLI wizard that aids in creating a new Ethereum network. | From 34bb132b108e0c17c079d6b24524c2958306a009 Mon Sep 17 00:00:00 2001 From: AmitBRD <60668103+AmitBRD@users.noreply.github.com> Date: Sun, 9 Feb 2020 15:50:44 -0500 Subject: [PATCH 3/8] graphql: add transaction signature values (#20623) The feature update allows the GraphQL API endpoint to retrieve transaction signature R,S,V parameters. Co-authored-by: amitshah Co-authored-by: Felix Lange --- graphql/graphql.go | 27 +++++++++++++++++++++++++++ graphql/schema.go | 3 +++ 2 files changed, 30 insertions(+) diff --git a/graphql/graphql.go b/graphql/graphql.go index ddd928dff1..0f7bba8657 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -314,6 +314,33 @@ func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) { return &ret, nil } +func (t *Transaction) R(ctx context.Context) (hexutil.Big, error) { + tx, err := t.resolve(ctx) + if err != nil || tx == nil { + return hexutil.Big{}, err + } + _, r, _ := tx.RawSignatureValues() + return hexutil.Big(*r), nil +} + +func (t *Transaction) S(ctx context.Context) (hexutil.Big, error) { + tx, err := t.resolve(ctx) + if err != nil || tx == nil { + return hexutil.Big{}, err + } + _, _, s := tx.RawSignatureValues() + return hexutil.Big(*s), nil +} + +func (t *Transaction) V(ctx context.Context) (hexutil.Big, error) { + tx, err := t.resolve(ctx) + if err != nil || tx == nil { + return hexutil.Big{}, err + } + v, _, _ := tx.RawSignatureValues() + return hexutil.Big(*v), nil +} + type BlockType int // Block represents an Ethereum block. diff --git a/graphql/schema.go b/graphql/schema.go index 525b9e1e5a..5dec10db20 100644 --- a/graphql/schema.go +++ b/graphql/schema.go @@ -115,6 +115,9 @@ const schema string = ` # Logs is a list of log entries emitted by this transaction. If the # transaction has not yet been mined, this field will be null. logs: [Log!] + r: BigInt! + s: BigInt! + v: BigInt! } # BlockFilterCriteria encapsulates log filter criteria for a filter applied From 172f7778fe498c383e0bdb234aaac4f61d4a2512 Mon Sep 17 00:00:00 2001 From: Adam Schmideg Date: Tue, 11 Feb 2020 09:48:58 +0100 Subject: [PATCH 4/8] rpc: add error when call result parameter is not addressable (#20638) --- rpc/client.go | 3 +++ rpc/client_test.go | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/rpc/client.go b/rpc/client.go index dd06b9469e..984f56bc69 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -276,6 +276,9 @@ func (c *Client) Call(result interface{}, method string, args ...interface{}) er // The result must be a pointer so that package json can unmarshal into it. You // can also pass nil, in which case the result is ignored. func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr { + return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result) + } msg, err := c.newMessage(method, args...) if err != nil { return err diff --git a/rpc/client_test.go b/rpc/client_test.go index 0d55140222..9c7f72053b 100644 --- a/rpc/client_test.go +++ b/rpc/client_test.go @@ -49,6 +49,23 @@ func TestClientRequest(t *testing.T) { } } +func TestClientResponseType(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + if err := client.Call(nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { + t.Errorf("Passing nil as result should be fine, but got an error: %v", err) + } + var resultVar echoResult + // Note: passing the var, not a ref + err := client.Call(resultVar, "test_echo", "hello", 10, &echoArgs{"world"}) + if err == nil { + t.Error("Passing a var as result should be an error") + } +} + func TestClientBatchRequest(t *testing.T) { server := newTestServer() defer server.Stop() From 8694d14e655a20d70e51b7c4605d63d53de8d351 Mon Sep 17 00:00:00 2001 From: chabashilah Date: Tue, 11 Feb 2020 18:52:51 +0900 Subject: [PATCH 5/8] signer: add bytes32 as valid primitive (#20609) --- signer/core/signed_data.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go index f512be7cea..3a827afa2d 100644 --- a/signer/core/signed_data.go +++ b/signer/core/signed_data.go @@ -923,7 +923,9 @@ func isPrimitiveTypeValid(primitiveType string) bool { primitiveType == "bytes30" || primitiveType == "bytes30[]" || primitiveType == "bytes31" || - primitiveType == "bytes31[]" { + primitiveType == "bytes31[]" || + primitiveType == "bytes32" || + primitiveType == "bytes32[]" { return true } if primitiveType == "int" || From dcffb7777fc2978cf1bb243f1ebdaa5f75d1e542 Mon Sep 17 00:00:00 2001 From: winsvega Date: Tue, 11 Feb 2020 10:54:05 +0100 Subject: [PATCH 6/8] cmd/geth retesteth: add eth_getBlockByHash (#20621) --- cmd/geth/retesteth.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/geth/retesteth.go b/cmd/geth/retesteth.go index 28e792bd77..ab7981e518 100644 --- a/cmd/geth/retesteth.go +++ b/cmd/geth/retesteth.go @@ -80,6 +80,7 @@ type RetestethEthAPI interface { SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error) BlockNumber(ctx context.Context) (uint64, error) GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) + GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error) GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error) GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error) @@ -618,6 +619,20 @@ func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexO return nil, fmt.Errorf("block %d not found", blockNr) } +func (api *RetestethAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { + block := api.blockchain.GetBlockByHash(blockHash) + if block != nil { + response, err := RPCMarshalBlock(block, true, fullTx) + if err != nil { + return nil, err + } + response["author"] = response["miner"] + response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), block.Number().Uint64())) + return response, err + } + return nil, fmt.Errorf("block 0x%x not found", blockHash) +} + func (api *RetestethAPI) AccountRange(ctx context.Context, blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, addressHash *math.HexOrDecimal256, maxResults uint64, From c22fdec3c7c48bc5076cb85b92a55a68951db167 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 11 Feb 2020 16:36:49 +0100 Subject: [PATCH 7/8] common/mclock: add NewTimer and Timer.Reset (#20634) These methods can be helpful when migrating existing timer code. --- common/mclock/mclock.go | 65 +++++++++++++-- common/mclock/simclock.go | 143 ++++++++++++++++++++++----------- common/mclock/simclock_test.go | 63 +++++++++++++-- 3 files changed, 207 insertions(+), 64 deletions(-) diff --git a/common/mclock/mclock.go b/common/mclock/mclock.go index d0e0cd78be..3aca257cb3 100644 --- a/common/mclock/mclock.go +++ b/common/mclock/mclock.go @@ -31,44 +31,93 @@ func Now() AbsTime { return AbsTime(monotime.Now()) } -// Add returns t + d. +// Add returns t + d as absolute time. func (t AbsTime) Add(d time.Duration) AbsTime { return t + AbsTime(d) } +// Sub returns t - t2 as a duration. +func (t AbsTime) Sub(t2 AbsTime) time.Duration { + return time.Duration(t - t2) +} + // The Clock interface makes it possible to replace the monotonic system clock with // a simulated clock. type Clock interface { Now() AbsTime Sleep(time.Duration) - After(time.Duration) <-chan time.Time + NewTimer(time.Duration) ChanTimer + After(time.Duration) <-chan AbsTime AfterFunc(d time.Duration, f func()) Timer } -// Timer represents a cancellable event returned by AfterFunc +// Timer is a cancellable event created by AfterFunc. type Timer interface { + // Stop cancels the timer. It returns false if the timer has already + // expired or been stopped. Stop() bool } +// ChanTimer is a cancellable event created by NewTimer. +type ChanTimer interface { + Timer + + // The channel returned by C receives a value when the timer expires. + C() <-chan AbsTime + // Reset reschedules the timer with a new timeout. + // It should be invoked only on stopped or expired timers with drained channels. + Reset(time.Duration) +} + // System implements Clock using the system clock. type System struct{} // Now returns the current monotonic time. -func (System) Now() AbsTime { +func (c System) Now() AbsTime { return AbsTime(monotime.Now()) } // Sleep blocks for the given duration. -func (System) Sleep(d time.Duration) { +func (c System) Sleep(d time.Duration) { time.Sleep(d) } +// NewTimer creates a timer which can be rescheduled. +func (c System) NewTimer(d time.Duration) ChanTimer { + ch := make(chan AbsTime, 1) + t := time.AfterFunc(d, func() { + // This send is non-blocking because that's how time.Timer + // behaves. It doesn't matter in the happy case, but does + // when Reset is misused. + select { + case ch <- c.Now(): + default: + } + }) + return &systemTimer{t, ch} +} + // After returns a channel which receives the current time after d has elapsed. -func (System) After(d time.Duration) <-chan time.Time { - return time.After(d) +func (c System) After(d time.Duration) <-chan AbsTime { + ch := make(chan AbsTime, 1) + time.AfterFunc(d, func() { ch <- c.Now() }) + return ch } // AfterFunc runs f on a new goroutine after the duration has elapsed. -func (System) AfterFunc(d time.Duration, f func()) Timer { +func (c System) AfterFunc(d time.Duration, f func()) Timer { return time.AfterFunc(d, f) } + +type systemTimer struct { + *time.Timer + ch <-chan AbsTime +} + +func (st *systemTimer) Reset(d time.Duration) { + st.Timer.Reset(d) +} + +func (st *systemTimer) C() <-chan AbsTime { + return st.ch +} diff --git a/common/mclock/simclock.go b/common/mclock/simclock.go index 4d351252ff..766ca0f873 100644 --- a/common/mclock/simclock.go +++ b/common/mclock/simclock.go @@ -17,6 +17,7 @@ package mclock import ( + "container/heap" "sync" "time" ) @@ -32,18 +33,24 @@ import ( // the timeout using a channel or semaphore. type Simulated struct { now AbsTime - scheduled []*simTimer + scheduled simTimerHeap mu sync.RWMutex cond *sync.Cond - lastId uint64 } -// simTimer implements Timer on the virtual clock. +// simTimer implements ChanTimer on the virtual clock. type simTimer struct { - do func() - at AbsTime - id uint64 - s *Simulated + at AbsTime + index int // position in s.scheduled + s *Simulated + do func() + ch <-chan AbsTime +} + +func (s *Simulated) init() { + if s.cond == nil { + s.cond = sync.NewCond(&s.mu) + } } // Run moves the clock by the given duration, executing all timers before that duration. @@ -53,14 +60,9 @@ func (s *Simulated) Run(d time.Duration) { end := s.now + AbsTime(d) var do []func() - for len(s.scheduled) > 0 { - ev := s.scheduled[0] - if ev.at > end { - break - } - s.now = ev.at + for len(s.scheduled) > 0 && s.scheduled[0].at <= end { + ev := heap.Pop(&s.scheduled).(*simTimer) do = append(do, ev.do) - s.scheduled = s.scheduled[1:] } s.now = end s.mu.Unlock() @@ -102,14 +104,22 @@ func (s *Simulated) Sleep(d time.Duration) { <-s.After(d) } +// NewTimer creates a timer which fires when the clock has advanced by d. +func (s *Simulated) NewTimer(d time.Duration) ChanTimer { + s.mu.Lock() + defer s.mu.Unlock() + + ch := make(chan AbsTime, 1) + var timer *simTimer + timer = s.schedule(d, func() { ch <- timer.at }) + timer.ch = ch + return timer +} + // After returns a channel which receives the current time after the clock // has advanced by d. -func (s *Simulated) After(d time.Duration) <-chan time.Time { - after := make(chan time.Time, 1) - s.AfterFunc(d, func() { - after <- (time.Time{}).Add(time.Duration(s.now)) - }) - return after +func (s *Simulated) After(d time.Duration) <-chan AbsTime { + return s.NewTimer(d).C() } // AfterFunc runs fn after the clock has advanced by d. Unlike with the system @@ -117,46 +127,83 @@ func (s *Simulated) After(d time.Duration) <-chan time.Time { func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer { s.mu.Lock() defer s.mu.Unlock() + + return s.schedule(d, fn) +} + +func (s *Simulated) schedule(d time.Duration, fn func()) *simTimer { s.init() at := s.now + AbsTime(d) - s.lastId++ - id := s.lastId - l, h := 0, len(s.scheduled) - ll := h - for l != h { - m := (l + h) / 2 - if (at < s.scheduled[m].at) || ((at == s.scheduled[m].at) && (id < s.scheduled[m].id)) { - h = m - } else { - l = m + 1 - } - } ev := &simTimer{do: fn, at: at, s: s} - s.scheduled = append(s.scheduled, nil) - copy(s.scheduled[l+1:], s.scheduled[l:ll]) - s.scheduled[l] = ev + heap.Push(&s.scheduled, ev) s.cond.Broadcast() return ev } func (ev *simTimer) Stop() bool { - s := ev.s - s.mu.Lock() - defer s.mu.Unlock() + ev.s.mu.Lock() + defer ev.s.mu.Unlock() - for i := 0; i < len(s.scheduled); i++ { - if s.scheduled[i] == ev { - s.scheduled = append(s.scheduled[:i], s.scheduled[i+1:]...) - s.cond.Broadcast() - return true - } + if ev.index < 0 { + return false } - return false + heap.Remove(&ev.s.scheduled, ev.index) + ev.s.cond.Broadcast() + ev.index = -1 + return true } -func (s *Simulated) init() { - if s.cond == nil { - s.cond = sync.NewCond(&s.mu) +func (ev *simTimer) Reset(d time.Duration) { + if ev.ch == nil { + panic("mclock: Reset() on timer created by AfterFunc") } + + ev.s.mu.Lock() + defer ev.s.mu.Unlock() + ev.at = ev.s.now.Add(d) + if ev.index < 0 { + heap.Push(&ev.s.scheduled, ev) // already expired + } else { + heap.Fix(&ev.s.scheduled, ev.index) // hasn't fired yet, reschedule + } + ev.s.cond.Broadcast() +} + +func (ev *simTimer) C() <-chan AbsTime { + if ev.ch == nil { + panic("mclock: C() on timer created by AfterFunc") + } + return ev.ch +} + +type simTimerHeap []*simTimer + +func (h *simTimerHeap) Len() int { + return len(*h) +} + +func (h *simTimerHeap) Less(i, j int) bool { + return (*h)[i].at < (*h)[j].at +} + +func (h *simTimerHeap) Swap(i, j int) { + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] + (*h)[i].index = i + (*h)[j].index = j +} + +func (h *simTimerHeap) Push(x interface{}) { + t := x.(*simTimer) + t.index = len(*h) + *h = append(*h, t) +} + +func (h *simTimerHeap) Pop() interface{} { + end := len(*h) - 1 + t := (*h)[end] + t.index = -1 + (*h)[end] = nil + *h = (*h)[:end] + return t } diff --git a/common/mclock/simclock_test.go b/common/mclock/simclock_test.go index 09e4391c1c..94aa4f2b39 100644 --- a/common/mclock/simclock_test.go +++ b/common/mclock/simclock_test.go @@ -25,14 +25,16 @@ var _ Clock = System{} var _ Clock = new(Simulated) func TestSimulatedAfter(t *testing.T) { - const timeout = 30 * time.Minute - const adv = time.Minute - var ( - c Simulated - end = c.Now().Add(timeout) - ch = c.After(timeout) + timeout = 30 * time.Minute + offset = 99 * time.Hour + adv = 11 * time.Minute + c Simulated ) + c.Run(offset) + + end := c.Now().Add(timeout) + ch := c.After(timeout) for c.Now() < end.Add(-adv) { c.Run(adv) select { @@ -45,8 +47,8 @@ func TestSimulatedAfter(t *testing.T) { c.Run(adv) select { case stamp := <-ch: - want := time.Time{}.Add(timeout) - if !stamp.Equal(want) { + want := AbsTime(0).Add(offset).Add(timeout) + if stamp != want { t.Errorf("Wrong time sent on timer channel: got %v, want %v", stamp, want) } default: @@ -113,3 +115,48 @@ func TestSimulatedSleep(t *testing.T) { t.Fatal("Sleep didn't return in time") } } + +func TestSimulatedTimerReset(t *testing.T) { + var ( + c Simulated + timeout = 1 * time.Hour + ) + timer := c.NewTimer(timeout) + c.Run(2 * timeout) + select { + case ftime := <-timer.C(): + if ftime != AbsTime(timeout) { + t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(timeout)) + } + default: + t.Fatal("timer didn't fire") + } + + timer.Reset(timeout) + c.Run(2 * timeout) + select { + case ftime := <-timer.C(): + if ftime != AbsTime(3*timeout) { + t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(3*timeout)) + } + default: + t.Fatal("timer didn't fire again") + } +} + +func TestSimulatedTimerStop(t *testing.T) { + var ( + c Simulated + timeout = 1 * time.Hour + ) + timer := c.NewTimer(timeout) + c.Run(2 * timeout) + if timer.Stop() { + t.Errorf("Stop returned true for fired timer") + } + select { + case <-timer.C(): + default: + t.Fatal("timer didn't fire") + } +} From 8045504abf64a865be4b1dbc780b796a9f5d11cc Mon Sep 17 00:00:00 2001 From: Adam Schmideg Date: Tue, 11 Feb 2020 16:46:32 +0100 Subject: [PATCH 8/8] les: log disconnect reason when light server is not synced (#20643) Co-authored-by: ligi --- les/server_handler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/les/server_handler.go b/les/server_handler.go index 38aeac4e88..98e9294b07 100644 --- a/les/server_handler.go +++ b/les/server_handler.go @@ -128,6 +128,7 @@ func (h *serverHandler) handle(p *peer) error { } // Reject light clients if server is not synced. if !h.synced() { + p.Log().Debug("Light server not synced, rejecting peer") return p2p.DiscRequested } defer p.fcClient.Disconnect()