Browse Source

rpc: fix /blockchain OOM #2049

pull/2056/head
Ethan Buchman 6 years ago
parent
commit
8dc655dad2
2 changed files with 94 additions and 15 deletions
  1. +36
    -15
      rpc/core/blocks.go
  2. +58
    -0
      rpc/core/blocks_test.go

+ 36
- 15
rpc/core/blocks.go View File

@ -64,25 +64,15 @@ import (
//
// <aside class="notice">Returns at most 20 items.</aside>
func BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
if minHeight == 0 {
minHeight = 1
}
if maxHeight == 0 {
maxHeight = blockStore.Height()
} else {
maxHeight = cmn.MinInt64(blockStore.Height(), maxHeight)
}
// maximum 20 block metas
const limit int64 = 20
minHeight = cmn.MaxInt64(minHeight, maxHeight-limit)
logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)
if minHeight > maxHeight {
return nil, fmt.Errorf("min height %d can't be greater than max height %d", minHeight, maxHeight)
var err error
minHeight, maxHeight, err = filterMinMax(blockStore.Height(), minHeight, maxHeight, limit)
if err != nil {
return nil, err
}
logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)
blockMetas := []*types.BlockMeta{}
for height := maxHeight; height >= minHeight; height-- {
@ -93,6 +83,37 @@ func BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, e
return &ctypes.ResultBlockchainInfo{blockStore.Height(), blockMetas}, nil
}
// error if either min or max are negative or min < max
// if 0, use 1 for min, latest block height for max
// enforce limit.
// error if min > max
func filterMinMax(height, min, max, limit int64) (int64, int64, error) {
// filter negatives
if min < 0 || max < 0 {
return min, max, fmt.Errorf("heights must be non-negative")
}
// adjust for default values
if min == 0 {
min = 1
}
if max == 0 {
max = height
}
// limit max to the height
max = cmn.MinInt64(height, max)
// limit min to within `limit` of max
// so the total number of blocks returned will be `limit`
min = cmn.MaxInt64(min, max-limit+1)
if min > max {
return min, max, fmt.Errorf("min height %d can't be greater than max height %d", min, max)
}
return min, max, nil
}
// Get block at a given height.
// If no height is provided, it will fetch the latest block.
//


+ 58
- 0
rpc/core/blocks_test.go View File

@ -0,0 +1,58 @@
package core
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestBlockchainInfo(t *testing.T) {
cases := []struct {
min, max int64
height int64
limit int64
resultLength int64
wantErr bool
}{
// min > max
{0, 0, 0, 10, 0, true}, // min set to 1
{0, 1, 0, 10, 0, true}, // max set to height (0)
{0, 0, 1, 10, 1, false}, // max set to height (1)
{2, 0, 1, 10, 0, true}, // max set to height (1)
{2, 1, 5, 10, 0, true},
// negative
{1, 10, 14, 10, 10, false}, // control
{-1, 10, 14, 10, 0, true},
{1, -10, 14, 10, 0, true},
{-9223372036854775808, -9223372036854775788, 100, 20, 0, true},
// check limit and height
{1, 1, 1, 10, 1, false},
{1, 1, 5, 10, 1, false},
{2, 2, 5, 10, 1, false},
{1, 2, 5, 10, 2, false},
{1, 5, 1, 10, 1, false},
{1, 5, 10, 10, 5, false},
{1, 15, 10, 10, 10, false},
{1, 15, 15, 10, 10, false},
{1, 15, 15, 20, 15, false},
{1, 20, 15, 20, 15, false},
{1, 20, 20, 20, 20, false},
}
for i, c := range cases {
caseString := fmt.Sprintf("test %d failed", i)
min, max, err := filterMinMax(c.height, c.min, c.max, c.limit)
if c.wantErr {
require.Error(t, err, caseString)
} else {
require.NoError(t, err, caseString)
require.Equal(t, 1+max-min, c.resultLength, caseString)
}
}
}

Loading…
Cancel
Save