Merge branch 'master' into les-test

This commit is contained in:
Adam Schmideg 2020-02-12 10:16:26 +01:00
commit 0158889e7d
12 changed files with 278 additions and 67 deletions

View file

@ -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. | | **`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. | | `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. | | `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. | | `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`). | | `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. | | `puppeth` | a CLI wizard that aids in creating a new Ethereum network. |

View file

@ -80,6 +80,7 @@ type RetestethEthAPI interface {
SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error) SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error)
BlockNumber(ctx context.Context) (uint64, error) BlockNumber(ctx context.Context) (uint64, error)
GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, 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) 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) 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) 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) 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, func (api *RetestethAPI) AccountRange(ctx context.Context,
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
addressHash *math.HexOrDecimal256, maxResults uint64, addressHash *math.HexOrDecimal256, maxResults uint64,

View file

@ -31,44 +31,93 @@ func Now() AbsTime {
return AbsTime(monotime.Now()) return AbsTime(monotime.Now())
} }
// Add returns t + d. // Add returns t + d as absolute time.
func (t AbsTime) Add(d time.Duration) AbsTime { func (t AbsTime) Add(d time.Duration) AbsTime {
return t + AbsTime(d) 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 // The Clock interface makes it possible to replace the monotonic system clock with
// a simulated clock. // a simulated clock.
type Clock interface { type Clock interface {
Now() AbsTime Now() AbsTime
Sleep(time.Duration) 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 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 { type Timer interface {
// Stop cancels the timer. It returns false if the timer has already
// expired or been stopped.
Stop() bool 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. // System implements Clock using the system clock.
type System struct{} type System struct{}
// Now returns the current monotonic time. // Now returns the current monotonic time.
func (System) Now() AbsTime { func (c System) Now() AbsTime {
return AbsTime(monotime.Now()) return AbsTime(monotime.Now())
} }
// Sleep blocks for the given duration. // Sleep blocks for the given duration.
func (System) Sleep(d time.Duration) { func (c System) Sleep(d time.Duration) {
time.Sleep(d) 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. // After returns a channel which receives the current time after d has elapsed.
func (System) After(d time.Duration) <-chan time.Time { func (c System) After(d time.Duration) <-chan AbsTime {
return time.After(d) 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. // 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) 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
}

View file

@ -17,6 +17,7 @@
package mclock package mclock
import ( import (
"container/heap"
"sync" "sync"
"time" "time"
) )
@ -32,18 +33,24 @@ import (
// the timeout using a channel or semaphore. // the timeout using a channel or semaphore.
type Simulated struct { type Simulated struct {
now AbsTime now AbsTime
scheduled []*simTimer scheduled simTimerHeap
mu sync.RWMutex mu sync.RWMutex
cond *sync.Cond cond *sync.Cond
lastId uint64
} }
// simTimer implements Timer on the virtual clock. // simTimer implements ChanTimer on the virtual clock.
type simTimer struct { type simTimer struct {
do func()
at AbsTime at AbsTime
id uint64 index int // position in s.scheduled
s *Simulated 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. // 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) end := s.now + AbsTime(d)
var do []func() var do []func()
for len(s.scheduled) > 0 { for len(s.scheduled) > 0 && s.scheduled[0].at <= end {
ev := s.scheduled[0] ev := heap.Pop(&s.scheduled).(*simTimer)
if ev.at > end {
break
}
s.now = ev.at
do = append(do, ev.do) do = append(do, ev.do)
s.scheduled = s.scheduled[1:]
} }
s.now = end s.now = end
s.mu.Unlock() s.mu.Unlock()
@ -102,14 +104,22 @@ func (s *Simulated) Sleep(d time.Duration) {
<-s.After(d) <-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 // After returns a channel which receives the current time after the clock
// has advanced by d. // has advanced by d.
func (s *Simulated) After(d time.Duration) <-chan time.Time { func (s *Simulated) After(d time.Duration) <-chan AbsTime {
after := make(chan time.Time, 1) return s.NewTimer(d).C()
s.AfterFunc(d, func() {
after <- (time.Time{}).Add(time.Duration(s.now))
})
return after
} }
// AfterFunc runs fn after the clock has advanced by d. Unlike with the system // 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 { func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.schedule(d, fn)
}
func (s *Simulated) schedule(d time.Duration, fn func()) *simTimer {
s.init() s.init()
at := s.now + AbsTime(d) 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} ev := &simTimer{do: fn, at: at, s: s}
s.scheduled = append(s.scheduled, nil) heap.Push(&s.scheduled, ev)
copy(s.scheduled[l+1:], s.scheduled[l:ll])
s.scheduled[l] = ev
s.cond.Broadcast() s.cond.Broadcast()
return ev return ev
} }
func (ev *simTimer) Stop() bool { func (ev *simTimer) Stop() bool {
s := ev.s ev.s.mu.Lock()
s.mu.Lock() defer ev.s.mu.Unlock()
defer s.mu.Unlock()
for i := 0; i < len(s.scheduled); i++ { if ev.index < 0 {
if s.scheduled[i] == ev {
s.scheduled = append(s.scheduled[:i], s.scheduled[i+1:]...)
s.cond.Broadcast()
return true
}
}
return false return false
} }
heap.Remove(&ev.s.scheduled, ev.index)
ev.s.cond.Broadcast()
ev.index = -1
return true
}
func (s *Simulated) init() { func (ev *simTimer) Reset(d time.Duration) {
if s.cond == nil { if ev.ch == nil {
s.cond = sync.NewCond(&s.mu) 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
} }

View file

@ -25,14 +25,16 @@ var _ Clock = System{}
var _ Clock = new(Simulated) var _ Clock = new(Simulated)
func TestSimulatedAfter(t *testing.T) { func TestSimulatedAfter(t *testing.T) {
const timeout = 30 * time.Minute
const adv = time.Minute
var ( var (
timeout = 30 * time.Minute
offset = 99 * time.Hour
adv = 11 * time.Minute
c Simulated c Simulated
end = c.Now().Add(timeout)
ch = c.After(timeout)
) )
c.Run(offset)
end := c.Now().Add(timeout)
ch := c.After(timeout)
for c.Now() < end.Add(-adv) { for c.Now() < end.Add(-adv) {
c.Run(adv) c.Run(adv)
select { select {
@ -45,8 +47,8 @@ func TestSimulatedAfter(t *testing.T) {
c.Run(adv) c.Run(adv)
select { select {
case stamp := <-ch: case stamp := <-ch:
want := time.Time{}.Add(timeout) want := AbsTime(0).Add(offset).Add(timeout)
if !stamp.Equal(want) { if stamp != want {
t.Errorf("Wrong time sent on timer channel: got %v, want %v", stamp, want) t.Errorf("Wrong time sent on timer channel: got %v, want %v", stamp, want)
} }
default: default:
@ -113,3 +115,48 @@ func TestSimulatedSleep(t *testing.T) {
t.Fatal("Sleep didn't return in time") 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")
}
}

View file

@ -314,6 +314,33 @@ func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) {
return &ret, nil 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 type BlockType int
// Block represents an Ethereum block. // Block represents an Ethereum block.

View file

@ -115,6 +115,9 @@ const schema string = `
# Logs is a list of log entries emitted by this transaction. If the # Logs is a list of log entries emitted by this transaction. If the
# transaction has not yet been mined, this field will be null. # transaction has not yet been mined, this field will be null.
logs: [Log!] logs: [Log!]
r: BigInt!
s: BigInt!
v: BigInt!
} }
# BlockFilterCriteria encapsulates log filter criteria for a filter applied # BlockFilterCriteria encapsulates log filter criteria for a filter applied

View file

@ -642,7 +642,7 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B
response, err := s.rpcMarshalBlock(block, true, fullTx) response, err := s.rpcMarshalBlock(block, true, fullTx)
if err == nil && number == rpc.PendingBlockNumber { if err == nil && number == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields // 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 response[field] = nil
} }
} }

View file

@ -128,6 +128,7 @@ func (h *serverHandler) handle(p *peer) error {
} }
// Reject light clients if server is not synced. // Reject light clients if server is not synced.
if !h.synced() { if !h.synced() {
p.Log().Debug("Light server not synced, rejecting peer")
return p2p.DiscRequested return p2p.DiscRequested
} }
defer p.fcClient.Disconnect() defer p.fcClient.Disconnect()

View file

@ -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 // 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. // 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 { 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...) msg, err := c.newMessage(method, args...)
if err != nil { if err != nil {
return err return err

View file

@ -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) { func TestClientBatchRequest(t *testing.T) {
server := newTestServer() server := newTestServer()
defer server.Stop() defer server.Stop()

View file

@ -923,7 +923,9 @@ func isPrimitiveTypeValid(primitiveType string) bool {
primitiveType == "bytes30" || primitiveType == "bytes30" ||
primitiveType == "bytes30[]" || primitiveType == "bytes30[]" ||
primitiveType == "bytes31" || primitiveType == "bytes31" ||
primitiveType == "bytes31[]" { primitiveType == "bytes31[]" ||
primitiveType == "bytes32" ||
primitiveType == "bytes32[]" {
return true return true
} }
if primitiveType == "int" || if primitiveType == "int" ||