mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Merge pull request #1 from ethereum/master
Update to go-ethereum master
This commit is contained in:
commit
96490a10be
14 changed files with 291 additions and 89 deletions
|
|
@ -127,15 +127,28 @@ func (b *SimulatedBackend) rollback() {
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stateByBlockNumber retrieves a state by a given blocknumber.
|
||||||
|
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
|
||||||
|
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 {
|
||||||
|
return b.blockchain.State()
|
||||||
|
}
|
||||||
|
block, err := b.BlockByNumber(ctx, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.blockchain.StateAt(block.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
// CodeAt returns the code associated with a certain account in the blockchain.
|
// CodeAt returns the code associated with a certain account in the blockchain.
|
||||||
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return nil, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
return statedb.GetCode(contract), nil
|
return statedb.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,10 +157,11 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return nil, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
return statedb.GetBalance(contract), nil
|
return statedb.GetBalance(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,10 +170,11 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return 0, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
return statedb.GetNonce(contract), nil
|
return statedb.GetNonce(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,10 +183,11 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return nil, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
val := statedb.GetState(contract, key)
|
val := statedb.GetState(contract, key)
|
||||||
return val[:], nil
|
return val[:], nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,11 @@ func (am *Manager) Wallets() []Wallet {
|
||||||
am.lock.RLock()
|
am.lock.RLock()
|
||||||
defer am.lock.RUnlock()
|
defer am.lock.RUnlock()
|
||||||
|
|
||||||
|
return am.walletsNoLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// walletsNoLock returns all registered wallets. Callers must hold am.lock.
|
||||||
|
func (am *Manager) walletsNoLock() []Wallet {
|
||||||
cpy := make([]Wallet, len(am.wallets))
|
cpy := make([]Wallet, len(am.wallets))
|
||||||
copy(cpy, am.wallets)
|
copy(cpy, am.wallets)
|
||||||
return cpy
|
return cpy
|
||||||
|
|
@ -155,7 +160,7 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, wallet := range am.Wallets() {
|
for _, wallet := range am.walletsNoLock() {
|
||||||
if wallet.URL() == parsed {
|
if wallet.URL() == parsed {
|
||||||
return wallet, nil
|
return wallet, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
index int // position in s.scheduled
|
||||||
id uint64
|
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 {
|
return false
|
||||||
s.scheduled = append(s.scheduled[:i], s.scheduled[i+1:]...)
|
|
||||||
s.cond.Broadcast()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
c Simulated
|
timeout = 30 * time.Minute
|
||||||
end = c.Now().Add(timeout)
|
offset = 99 * time.Hour
|
||||||
ch = c.After(timeout)
|
adv = 11 * time.Minute
|
||||||
|
c Simulated
|
||||||
)
|
)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ func (f *Feed) Send(value interface{}) (nsent int) {
|
||||||
|
|
||||||
if !f.typecheck(rvalue.Type()) {
|
if !f.typecheck(rvalue.Type()) {
|
||||||
f.sendLock <- struct{}{}
|
f.sendLock <- struct{}{}
|
||||||
|
f.mu.Unlock()
|
||||||
panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype})
|
panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype})
|
||||||
}
|
}
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,6 @@ func (s *resubscribeSub) loop() {
|
||||||
func (s *resubscribeSub) subscribe() Subscription {
|
func (s *resubscribeSub) subscribe() Subscription {
|
||||||
subscribed := make(chan error)
|
subscribed := make(chan error)
|
||||||
var sub Subscription
|
var sub Subscription
|
||||||
retry:
|
|
||||||
for {
|
for {
|
||||||
s.lastTry = mclock.Now()
|
s.lastTry = mclock.Now()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
@ -157,19 +156,19 @@ retry:
|
||||||
select {
|
select {
|
||||||
case err := <-subscribed:
|
case err := <-subscribed:
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
// Subscribing failed, wait before launching the next try.
|
if sub == nil {
|
||||||
if s.backoffWait() {
|
panic("event: ResubscribeFunc returned nil subscription and no error")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
continue retry
|
return sub
|
||||||
}
|
}
|
||||||
if sub == nil {
|
// Subscribing failed, wait before launching the next try.
|
||||||
panic("event: ResubscribeFunc returned nil subscription and no error")
|
if s.backoffWait() {
|
||||||
|
return nil // unsubscribed during wait
|
||||||
}
|
}
|
||||||
return sub
|
|
||||||
case <-s.unsub:
|
case <-s.unsub:
|
||||||
cancel()
|
cancel()
|
||||||
|
<-subscribed // avoid leaking the s.fn goroutine.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ func TestResubscribe(t *testing.T) {
|
||||||
func TestResubscribeAbort(t *testing.T) {
|
func TestResubscribeAbort(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
done := make(chan error)
|
done := make(chan error, 1)
|
||||||
sub := Resubscribe(0, func(ctx context.Context) (Subscription, error) {
|
sub := Resubscribe(0, func(ctx context.Context) (Subscription, error) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ func (n *ExecNode) Stop() error {
|
||||||
if err := n.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
if err := n.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
||||||
return n.Cmd.Process.Kill()
|
return n.Cmd.Process.Kill()
|
||||||
}
|
}
|
||||||
waitErr := make(chan error)
|
waitErr := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
waitErr <- n.Cmd.Wait()
|
waitErr <- n.Cmd.Wait()
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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" ||
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue