Skip to content

Commit

Permalink
Update check tip util functions to return both at-tip status and bloc…
Browse files Browse the repository at this point in the history
…k id (#294)

* return at tip status along side block identifier in the check*tip util methods

* fix comment

* return nil identifier if error
  • Loading branch information
swapna gupta authored Jan 12, 2021
1 parent 3a557da commit d931a3a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 60 deletions.
46 changes: 27 additions & 19 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@ type BlockStorageHelper interface {
// to this interface.
}

// CheckNetworkTip returns block identifier if the block returned by network/status
// endpoint is at tip. Note that the tipDelay param takes tip delay in seconds.
// CheckNetworkTip returns a boolean indicating if the block returned by
// network/status is at tip. It also returns the block identifier
// returned by network/status.
// Note that the tipDelay param takes tip delay in seconds.
// Block returned by network/status is considered to be at tip if one of the
// following two conditions is met:
// (1) the block was produced within tipDelay of current time
Expand All @@ -208,64 +210,70 @@ func CheckNetworkTip(ctx context.Context,
network *types.NetworkIdentifier,
tipDelay int64,
f FetcherHelper,
) (*types.BlockIdentifier, error) {
) (bool, *types.BlockIdentifier, error) {
// todo: refactor CheckNetworkTip and its usages to accept metadata and pass it to
// NetworkStatusRetry call.
status, fetchErr := f.NetworkStatusRetry(ctx, network, nil)
if fetchErr != nil {
return nil, fmt.Errorf("%w: unable to fetch network status", fetchErr.Err)
return false, nil, fmt.Errorf("%w: unable to fetch network status", fetchErr.Err)
}

// if the block timestamp is within tip delay of current time,
// it can be considered to be at tip.
if AtTip(tipDelay, status.CurrentBlockTimestamp) {
return status.CurrentBlockIdentifier, nil
return true, status.CurrentBlockIdentifier, nil
}

// If the sync status returned by network/status is true, we should consider the block to be at
// tip.
if status.SyncStatus != nil && status.SyncStatus.Synced != nil && *status.SyncStatus.Synced {
return status.CurrentBlockIdentifier, nil
return true, status.CurrentBlockIdentifier, nil
}

return nil, nil
return false, status.CurrentBlockIdentifier, nil
}

// CheckStorageTip returns block identifier if the current block
// in storage is at tip. Note that the tipDelay param takes tip delay in seconds.
// A block in storage is considered to be at tip if one of the following two conditions is met
// CheckStorageTip returns a boolean indicating if the current
// block returned by block storage helper is at tip. It also
// returns the block identifier of the current storage block.
// Note that the tipDelay param takes tip delay in seconds.
// A block in storage is considered to be at tip if one of the
// following two conditions is met:
// (1) the block was produced within tipDelay of current time
// (i.e. block timestamp >= current time - tipDelay)
// (2) CheckNetworkTip returns the same block as the current block in storage
// (2) CheckNetworkTip returns true and the block it returns
// is same as the current block in storage
func CheckStorageTip(ctx context.Context,
network *types.NetworkIdentifier,
tipDelay int64,
f FetcherHelper,
b BlockStorageHelper,
) (*types.BlockIdentifier, error) {
) (bool, *types.BlockIdentifier, error) {
blockResponse, err := b.GetBlockLazy(ctx, nil)
if errors.Is(err, storageErrors.ErrHeadBlockNotFound) {
// If no blocks exist in storage yet, we are not at tip
return nil, nil
return false, nil, nil
}

currentStorageBlock := blockResponse.Block
if AtTip(tipDelay, currentStorageBlock.Timestamp) {
return currentStorageBlock.BlockIdentifier, nil
return true, currentStorageBlock.BlockIdentifier, nil
}

// if latest block in storage is not at tip,
// check network status
tipBlock, fetchErr := CheckNetworkTip(ctx, network, tipDelay, f)
networkAtTip, tipBlock, fetchErr := CheckNetworkTip(ctx, network, tipDelay, f)
if fetchErr != nil {
return nil, fmt.Errorf("%w: unable to fetch network status", fetchErr)
return false,
nil,
fmt.Errorf("%w: unable to fetch network status", fetchErr)
}

if types.Hash(tipBlock) == types.Hash(currentStorageBlock.BlockIdentifier) {
return currentStorageBlock.BlockIdentifier, nil
if networkAtTip && types.Hash(tipBlock) == types.Hash(currentStorageBlock.BlockIdentifier) {
return true, currentStorageBlock.BlockIdentifier, nil
}

return nil, nil
return false, currentStorageBlock.BlockIdentifier, nil
}

// CheckNetworkSupported checks if a Rosetta implementation supports a given
Expand Down
95 changes: 54 additions & 41 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,9 @@ func TestCheckNetworkTip(t *testing.T) {
helper *mocks.FetcherHelper
tipDelay int64

expectedResult *types.BlockIdentifier
expectedError error
expectedAtTip bool
expectedIdentifier *types.BlockIdentifier
expectedError error
}{
"at tip": {
helper: func() *mocks.FetcherHelper {
Expand All @@ -412,9 +413,10 @@ func TestCheckNetworkTip(t *testing.T) {

return mockHelper
}(),
tipDelay: 100,
expectedResult: blockIdentifier,
expectedError: nil,
tipDelay: 100,
expectedAtTip: true,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"not at tip": {
helper: func() *mocks.FetcherHelper {
Expand All @@ -435,9 +437,10 @@ func TestCheckNetworkTip(t *testing.T) {

return mockHelper
}(),
tipDelay: 100,
expectedResult: nil,
expectedError: nil,
tipDelay: 100,
expectedAtTip: false,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"synced tip": {
helper: func() *mocks.FetcherHelper {
Expand All @@ -461,9 +464,10 @@ func TestCheckNetworkTip(t *testing.T) {

return mockHelper
}(),
tipDelay: 100,
expectedResult: blockIdentifier,
expectedError: nil,
tipDelay: 100,
expectedAtTip: true,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"not synced tip": {
helper: func() *mocks.FetcherHelper {
Expand All @@ -487,9 +491,10 @@ func TestCheckNetworkTip(t *testing.T) {

return mockHelper
}(),
tipDelay: 100,
expectedResult: nil,
expectedError: nil,
tipDelay: 100,
expectedAtTip: false,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"error": {
helper: func() *mocks.FetcherHelper {
Expand All @@ -510,18 +515,21 @@ func TestCheckNetworkTip(t *testing.T) {
return mockHelper
}(),
tipDelay: 100,
expectedAtTip: false,
expectedError: fetcher.ErrRequestFailed,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
tipBlock, err := CheckNetworkTip(ctx, network, test.tipDelay, test.helper)
atTip, tipBlock, err := CheckNetworkTip(ctx, network, test.tipDelay, test.helper)
if test.expectedError != nil {
assert.False(t, atTip)
assert.Nil(t, tipBlock)
assert.True(t, errors.Is(err, test.expectedError))
} else {
assert.Equal(t, test.expectedResult, tipBlock)
assert.Equal(t, test.expectedAtTip, atTip)
assert.Equal(t, test.expectedIdentifier, tipBlock)
assert.NoError(t, err)
}
test.helper.AssertExpectations(t)
Expand All @@ -537,8 +545,9 @@ func TestCheckStorageTip(t *testing.T) {
b *mocks.BlockStorageHelper
tipDelay int64

expectedResult *types.BlockIdentifier
expectedError error
expectedAtTip bool
expectedIdentifier *types.BlockIdentifier
expectedError error
}{
"stored block within tip delay": {
f: &mocks.FetcherHelper{},
Expand All @@ -559,9 +568,10 @@ func TestCheckStorageTip(t *testing.T) {
).Once()
return mockHelper
}(),
tipDelay: 1000,
expectedResult: blockIdentifier,
expectedError: nil,
tipDelay: 1000,
expectedAtTip: true,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"stored block not within tip delay, network synced ": {
f: func() *mocks.FetcherHelper {
Expand Down Expand Up @@ -595,11 +605,12 @@ func TestCheckStorageTip(t *testing.T) {
).Once()
return mockHelper
}(),
tipDelay: 100,
expectedResult: blockIdentifier,
expectedError: nil,
tipDelay: 100,
expectedAtTip: true,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"stored block not within tip delay, network not synced ": {
"stored block not within tip delay, network not synced": {
f: func() *mocks.FetcherHelper {
mockHelper := &mocks.FetcherHelper{}
mockHelper.On("NetworkStatusRetry",
Expand Down Expand Up @@ -631,9 +642,10 @@ func TestCheckStorageTip(t *testing.T) {
).Once()
return mockHelper
}(),
tipDelay: 100,
expectedResult: nil,
expectedError: nil,
tipDelay: 100,
expectedAtTip: false,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"stored block and network blocks different": {
f: func() *mocks.FetcherHelper {
Expand Down Expand Up @@ -662,20 +674,18 @@ func TestCheckStorageTip(t *testing.T) {
).Return(
&types.BlockResponse{
Block: &types.Block{
BlockIdentifier: &types.BlockIdentifier{
Hash: "block",
Index: 1,
},
Timestamp: Milliseconds() - 300*MillisecondsInSecond,
BlockIdentifier: blockIdentifier,
Timestamp: Milliseconds() - 300*MillisecondsInSecond,
},
},
nil,
).Once()
return mockHelper
}(),
tipDelay: 100,
expectedResult: nil,
expectedError: nil,
tipDelay: 100,
expectedAtTip: false,
expectedIdentifier: blockIdentifier,
expectedError: nil,
},
"no blocks in storage": {
f: &mocks.FetcherHelper{},
Expand All @@ -691,20 +701,23 @@ func TestCheckStorageTip(t *testing.T) {
).Once()
return mockHelper
}(),
tipDelay: 100,
expectedResult: nil,
expectedError: nil,
tipDelay: 100,
expectedAtTip: false,
expectedIdentifier: nil,
expectedError: nil,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
tipBlock, err := CheckStorageTip(ctx, network, test.tipDelay, test.f, test.b)
atTip, tipBlock, err := CheckStorageTip(ctx, network, test.tipDelay, test.f, test.b)
if test.expectedError != nil {
assert.False(t, atTip)
assert.Nil(t, tipBlock)
assert.True(t, errors.Is(err, test.expectedError))
} else {
assert.Equal(t, test.expectedResult, tipBlock)
assert.Equal(t, test.expectedAtTip, atTip)
assert.Equal(t, test.expectedIdentifier, tipBlock)
assert.NoError(t, err)
}
test.f.AssertExpectations(t)
Expand Down

0 comments on commit d931a3a

Please sign in to comment.