Simplify error handling

This commit is contained in:
Sean Braithwaite
2019-07-19 01:44:27 +03:00
parent 18fcda8c93
commit 5f84e3ecf5
2 changed files with 68 additions and 82 deletions

View File

@@ -1,7 +1,6 @@
package v2
import (
"errors"
"fmt"
"math"
"math/rand"
@@ -13,36 +12,6 @@ import (
type height int64
// errors
var (
// new
errDuplicatePeer = errors.New("fast sync tried to add a peer twice")
errPeerNotFound = errors.New("Peer not found")
errPeerRemoved = errors.New("try to remove a removed peer")
errBadSchedule = errors.New("Invalid Schedule transition")
// internal to the package
errNoErrorFinished = errors.New("fast sync is finished")
errInvalidEvent = errors.New("invalid event in current state")
errMissingBlock = errors.New("missing blocks")
errNilPeerForBlockRequest = errors.New("peer for block request does not exist in the switch")
errSendQueueFull = errors.New("block request not made, send-queue is full")
errPeerTooShort = errors.New("peer height too low, old peer removed/ new peer not added")
errSwitchRemovesPeer = errors.New("switch is removing peer")
errTimeoutEventWrongState = errors.New("timeout event for a state different than the current one")
errNoTallerPeer = errors.New("fast sync timed out on waiting for a peer taller than this node")
// reported eventually to the switch
errPeerLowersItsHeight = errors.New("fast sync peer reports a height lower than previous") // handle return
errNoPeerResponseForCurrentHeights = errors.New("fast sync timed out on peer block response for current heights") // handle return
errNoPeerResponse = errors.New("fast sync timed out on peer block response") // xx
errBadDataFromPeer = errors.New("fast sync received block from wrong peer or block is bad") // xx
errDuplicateBlock = errors.New("fast sync received duplicate block from peer")
errBlockVerificationFailure = errors.New("fast sync block verification failure") // xx
errSlowPeer = errors.New("fast sync peer is not sending us data fast enough") // xx
)
type Event interface{}
type schedulerErrorEv struct {
peerID p2p.ID
@@ -72,8 +41,7 @@ func (e blockState) String() string {
case blockStateProcessed:
return "Processed"
default:
// XXX: panic?
return fmt.Sprintf("default %d", e)
return fmt.Sprintf("unknown blockState: %d", e)
}
}
@@ -90,6 +58,19 @@ const (
peerStateRemoved
)
func (e peerState) String() string {
switch e {
case peerStateNew:
return "New"
case peerStateReady:
return "Ready"
case peerStateRemoved:
return "Removed"
default:
return fmt.Sprintf("unknown peerState: %d", e)
}
}
type scPeer struct {
peerID p2p.ID
state peerState
@@ -142,7 +123,7 @@ func newSchedule(initHeight int64) *schedule {
func (sc *schedule) addPeer(peerID p2p.ID) error {
if _, ok := sc.peers[peerID]; ok {
return errDuplicatePeer
return fmt.Errorf("Cannot add duplicate peer %s", peerID)
}
sc.peers[peerID] = newScPeer(peerID)
return nil
@@ -150,8 +131,12 @@ func (sc *schedule) addPeer(peerID p2p.ID) error {
func (sc *schedule) touchPeer(peerID p2p.ID, time time.Time) error {
peer, ok := sc.peers[peerID]
if !ok || peer.state == peerStateRemoved {
return errPeerNotFound
if !ok {
return fmt.Errorf("Couldn't find peer %s", peerID)
}
if peer.state == peerStateRemoved {
return fmt.Errorf("Tried to touch peer in peerStateRemoved")
}
peer.lastTouched = time
@@ -161,12 +146,12 @@ func (sc *schedule) touchPeer(peerID p2p.ID, time time.Time) error {
func (sc *schedule) removePeer(peerID p2p.ID) error {
peer, ok := sc.peers[peerID]
if !ok || peer.state == peerStateRemoved {
return errPeerNotFound
if !ok {
return fmt.Errorf("Couldn't find peer %s", peerID)
}
if peer.state == peerStateRemoved {
return errPeerRemoved
return fmt.Errorf("Tried to remove peer %s in peerStateRemoved", peerID)
}
for height, pendingPeerID := range sc.pendingBlocks {
@@ -183,12 +168,16 @@ func (sc *schedule) removePeer(peerID p2p.ID) error {
func (sc *schedule) setPeerHeight(peerID p2p.ID, height int64) error {
peer, ok := sc.peers[peerID]
if !ok || peer.state == peerStateRemoved {
return errPeerNotFound
if !ok {
return fmt.Errorf("Can't find peer %s", peerID)
}
if peer.state == peerStateRemoved {
return fmt.Errorf("Cannot set peer height for a peer in peerStateRemoved")
}
if height < peer.height {
return errPeerLowersItsHeight
return fmt.Errorf("Cannot move peer height lower. from %d to %d", peer.height, height)
}
peer.height = height
@@ -249,29 +238,24 @@ func (sc *schedule) setStateAtHeight(height int64, state blockState) {
sc.blockStates[height] = state
}
// TODO keep track of when i received this block
func (sc *schedule) markReceived(peerID p2p.ID, height int64, size int64, now time.Time) error {
peer, ok := sc.peers[peerID]
if !ok || peer.state != peerStateReady {
return errPeerNotFound
if !ok {
return fmt.Errorf("Can't find peer %s", peerID)
}
// it looks like height and size are being interchanged s
if state := sc.getStateAtHeight(height); state != blockStatePending {
// received a block not in pending
// XXX: can we have more specialized errors here?
return errBadSchedule
if peer.state == peerStateRemoved {
return fmt.Errorf("Cannot receive blocks from removed peer %s", peerID)
}
// check if the block is pending from that peer
if sc.pendingBlocks[height] != peerID {
return errBadSchedule
if state := sc.getStateAtHeight(height); state != blockStatePending || sc.pendingBlocks[height] != peerID {
return fmt.Errorf("Received block %d from peer %s without being requested", height, peerID)
}
pendingTime, ok := sc.pendingTime[height]
if !ok || now.Sub(pendingTime) <= 0 {
// xxx: better errors
return errBadSchedule
return fmt.Errorf("Clock error. Block %d received at %s but requested at %s",
height, pendingTime, now)
}
peer.lastRate = size / int64(now.Sub(pendingTime).Seconds())
@@ -288,13 +272,17 @@ func (sc *schedule) markReceived(peerID p2p.ID, height int64, size int64, now ti
// todo keep track of when i requested this block
func (sc *schedule) markPending(peerID p2p.ID, height int64, time time.Time) error {
peer, ok := sc.peers[peerID]
if !ok || peer.state != peerStateReady {
return errPeerNotFound
if !ok {
return fmt.Errorf("Can't find peer %s", peerID)
}
if peer.state != peerStateReady {
return fmt.Errorf("Cannot schedule %d from %s in %s", height, peerID, peer.state)
}
if height > peer.height {
// tried to request a block from a peer who doesn't have it
return errBadSchedule
return fmt.Errorf("Cannot request height %d from peer %s who is at height %d",
height, peerID, peer.height)
}
sc.setStateAtHeight(height, blockStatePending)
@@ -307,8 +295,9 @@ func (sc *schedule) markPending(peerID p2p.ID, height int64, time time.Time) err
}
func (sc *schedule) markProcessed(height int64) error {
if sc.getStateAtHeight(height) != blockStateReceived {
return errBadSchedule
state := sc.getStateAtHeight(height)
if state != blockStateReceived {
return fmt.Errorf("Can't mark height %d received from block state %s", height, state)
}
sc.setStateAtHeight(height, blockStateProcessed)
@@ -364,7 +353,7 @@ func (sc *schedule) pendingFrom(peerID p2p.ID) []int64 {
// set any blocks in blockStatePending or blockStateReceived by peerID to blockStateNew
func (sc *schedule) resetBlocks(peerID p2p.ID) error {
if _, ok := sc.peers[peerID]; !ok {
return errPeerNotFound
return fmt.Errorf("Can't find peer %s", peerID)
}
// this should use pendingFrom

View File

@@ -29,14 +29,14 @@ func TestAddPeer(t *testing.T) {
assert.Nil(t, sc.addPeer(peerID))
assert.Nil(t, sc.addPeer(peerIDTwo))
assert.Equal(t, sc.addPeer(peerID), errDuplicatePeer)
assert.Error(t, sc.addPeer(peerID))
}
func TestTouchPeer(t *testing.T) {
sc := newSchedule(initHeight)
now := time.Now()
assert.Equal(t, errPeerNotFound, sc.touchPeer(peerID, now),
assert.Error(t, sc.touchPeer(peerID, now),
"Touching an unknown peer should return errPeerNotFound")
assert.Nil(t, sc.addPeer(peerID),
@@ -76,20 +76,20 @@ func TestHeightFSM(t *testing.T) {
assert.Nil(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.Equal(t, errPeerNotFound, sc.markPending(peerID, peerHeight, now),
"Expected markingPending on an unknown peer to return errPeerNotFound")
assert.Error(t, sc.markPending(peerID, peerHeight, now),
"Expected markingPending on an unknown peer to return an error")
assert.Nil(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight to return no error")
assert.Equal(t, errBadSchedule, sc.markReceived(peerID, peerHeight, blockSize, now.Add(1*time.Second)),
assert.Error(t, sc.markReceived(peerID, peerHeight, blockSize, now.Add(1*time.Second)),
"Expecting transitioning from blockStateNew to blockStateReceived to fail")
assert.Equal(t, errBadSchedule, sc.markProcessed(peerHeight),
assert.Error(t, sc.markProcessed(peerHeight),
"Expecting transitioning from blockStateNew to blockStateReceived to fail")
assert.Equal(t, blockStateUnknown, sc.getStateAtHeight(peerHeight+10),
"Expected the maximum height seen + 10 to be in blockStateUnknown")
assert.Equal(t, errBadSchedule, sc.markPending(peerID, peerHeight+10, now.Add(1*time.Second)),
assert.Error(t, sc.markPending(peerID, peerHeight+10, now.Add(1*time.Second)),
"Expected markPending on block in blockStateUnknown height to fail")
assert.Nil(t, sc.markPending(peerID, initHeight, now.Add(1*time.Second)),
"Expected markPending on a known height with a known peer to return no error")
@@ -100,21 +100,21 @@ func TestHeightFSM(t *testing.T) {
"Expected marking markReceived on a pending block to return no error")
// here we are trying to reset blocks that were received
assert.Nil(t, sc.resetBlocks(peerID),
assert.NoError(t, sc.resetBlocks(peerID),
"Expected resetBlocks to return no error")
assert.Equal(t, blockStateNew, sc.getStateAtHeight(initHeight),
"Expected blocks to be in blockStateNew after being reset")
assert.Nil(t, sc.markPending(peerID, initHeight, now),
assert.NoError(t, sc.markPending(peerID, initHeight, now),
"Expected marking a reset block to pending to return no error")
assert.Equal(t, blockStatePending, sc.getStateAtHeight(initHeight),
"Expected block to be in blockStatePending")
assert.Nil(t, sc.markReceived(peerID, initHeight, blockSize, now.Add(2*time.Second)),
assert.NoError(t, sc.markReceived(peerID, initHeight, blockSize, now.Add(2*time.Second)),
"Expected marking a pending block as received to return no error")
assert.Equal(t, blockStateReceived, sc.getStateAtHeight(initHeight))
assert.Nil(t, sc.markProcessed(initHeight),
assert.NoError(t, sc.markProcessed(initHeight),
"Expected marking a block as processed to success")
assert.Equal(t, blockStateProcessed, sc.getStateAtHeight(initHeight),
"Expected the block to in blockStateProcessed")
@@ -130,13 +130,10 @@ func TestMinMaxHeight(t *testing.T) {
assert.Equal(t, initHeight, sc.maxHeight(),
"Expected max height to be the initialized height")
assert.Equal(t, initHeight, sc.maxHeight(),
"Expected max height to be the initialized height")
assert.Nil(t, sc.addPeer(peerID),
assert.NoError(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.Nil(t, sc.setPeerHeight(peerID, peerHeight),
assert.NoError(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight to return no error")
assert.Equal(t, peerHeight, sc.maxHeight(),
@@ -154,16 +151,16 @@ func TestPeersSlowerThan(t *testing.T) {
now := time.Now()
receivedAt := now.Add(1 * time.Second)
assert.Nil(t, sc.addPeer(peerID),
assert.NoError(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.Nil(t, sc.setPeerHeight(peerID, peerHeight),
assert.NoError(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight to return no error")
assert.Nil(t, sc.markPending(peerID, peerHeight, now),
assert.NoError(t, sc.markPending(peerID, peerHeight, now),
"Expected markingPending on to return no error")
assert.Nil(t, sc.markReceived(peerID, peerHeight, blockSize, receivedAt),
assert.NoError(t, sc.markReceived(peerID, peerHeight, blockSize, receivedAt),
"Expected markingPending on to return no error")
assert.Empty(t, sc.peersSlowerThan(blockSize-1),