Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions database/query/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ type Interval uint8

const (
IntervalRaw Interval = iota
IntervalSecond
IntervalMinute
IntervalHour
IntervalDay
IntervalWeek
Expand All @@ -16,6 +18,8 @@ const (

var AllIntervals = []Interval{
IntervalRaw,
IntervalSecond,
IntervalMinute,
IntervalHour,
IntervalDay,
IntervalWeek,
Expand All @@ -26,6 +30,10 @@ func ToInterval(val string) (Interval, error) {
switch val {
case "raw":
return IntervalRaw, nil
case "second":
return IntervalSecond, nil
case "minute":
return IntervalMinute, nil
case "hour":
return IntervalHour, nil
case "day":
Expand All @@ -43,6 +51,10 @@ func FromInterval(val Interval) (string, error) {
switch val {
case IntervalRaw:
return "raw", nil
case IntervalSecond:
return "second", nil
case IntervalMinute:
return "minute", nil
case IntervalHour:
return "hour", nil
case IntervalDay:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
filippo.io/edwards25519 v1.1.0
github.com/aws/aws-sdk-go-v2 v0.17.0
github.com/code-payments/code-vm-indexer v1.2.0
github.com/code-payments/ocp-protobuf-api v0.7.0
github.com/code-payments/ocp-protobuf-api v0.8.0
github.com/emirpasic/gods v1.12.0
github.com/envoyproxy/protoc-gen-validate v1.2.1
github.com/golang/protobuf v1.5.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/code-payments/code-vm-indexer v1.2.0 h1:rSHpBMiT9BKgmKcXg/VIoi/h0t7jNxGx07Qz59m+6Q0=
github.com/code-payments/code-vm-indexer v1.2.0/go.mod h1:vn91YN2qNqb+gGJeZe2+l+TNxVmEEiRHXXnIn2Y40h8=
github.com/code-payments/ocp-protobuf-api v0.7.0 h1:pHIVYXmDus32LEzaj92qDWKYrPawuzBIJ+Xlzzf9udg=
github.com/code-payments/ocp-protobuf-api v0.7.0/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8=
github.com/code-payments/ocp-protobuf-api v0.8.0 h1:Fm1dH7dFQu/xn5ohuPrJqcju8pWtIi4upH5f97KVrvY=
github.com/code-payments/ocp-protobuf-api v0.8.0/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
Expand Down
29 changes: 29 additions & 0 deletions ocp/data/currency/memory/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,32 @@ func (s *store) GetReserveAtTime(ctx context.Context, mint string, t time.Time)

return results[0].Clone(), nil
}

func (s *store) GetReservesInRange(ctx context.Context, mint string, interval query.Interval, start time.Time, end time.Time, ordering query.Ordering) ([]*currency.ReserveRecord, error) {
s.mu.Lock()
defer s.mu.Unlock()

sort.Sort(ReserveByTime(s.reserveRecords))

// Not ideal but fine for testing the currency store
var all []*currency.ReserveRecord
for _, item := range s.reserveRecords {
if item.Mint == mint && item.Time.Unix() >= start.Unix() && item.Time.Unix() <= end.Unix() {
all = append(all, item.Clone())
}
}

// TODO: handle the interval

if len(all) == 0 {
return nil, currency.ErrNotFound
}

if ordering == query.Ascending {
for i, j := 0, len(all)-1; i < j; i, j = i+1, j-1 {
all[i], all[j] = all[j], all[i]
}
}

return all, nil
}
17 changes: 17 additions & 0 deletions ocp/data/currency/postgres/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,20 @@ func dbGetReserveByMintAndTime(ctx context.Context, db *sqlx.DB, mint string, t
)
return res, pgutil.CheckNoRows(err, currency.ErrNotFound)
}

func dbGetAllReservesForRange(ctx context.Context, db *sqlx.DB, mint string, interval q.Interval, start time.Time, end time.Time, ordering q.Ordering) ([]*reserveModel, error) {
res := []*reserveModel{}
err := db.SelectContext(ctx, &res,
makeTimeBasedRangeQuery(reserveTableName, "mint = $1 AND for_timestamp >= $2 AND for_timestamp <= $3", ordering, interval),
mint, start.UTC(), end.UTC(),
)

if err != nil {
return nil, pgutil.CheckNoRows(err, currency.ErrNotFound)
}
if len(res) == 0 {
return nil, currency.ErrNotFound
}

return res, nil
}
31 changes: 31 additions & 0 deletions ocp/data/currency/postgres/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,34 @@ func (s *store) GetReserveAtTime(ctx context.Context, mint string, t time.Time)
}
return fromReserveModel(model), nil
}

func (s *store) GetReservesInRange(ctx context.Context, mint string, interval query.Interval, start time.Time, end time.Time, ordering query.Ordering) ([]*currency.ReserveRecord, error) {
if interval > query.IntervalMonth {
return nil, currency.ErrInvalidInterval
}

if start.IsZero() || end.IsZero() {
return nil, currency.ErrInvalidRange
}

var actualStart, actualEnd time.Time
if start.Unix() > end.Unix() {
actualStart = end
actualEnd = start
} else {
actualStart = start
actualEnd = end
}

list, err := dbGetAllReservesForRange(ctx, s.db, mint, interval, actualStart, actualEnd, ordering)
if err != nil {
return nil, err
}

res := []*currency.ReserveRecord{}
for _, item := range list {
res = append(res, fromReserveModel(item))
}

return res, nil
}
9 changes: 9 additions & 0 deletions ocp/data/currency/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,13 @@ type Store interface {
//
// ErrNotFound is returned if no reserve data was found for the provided Timestamp.
GetReserveAtTime(ctx context.Context, mint string, t time.Time) (*ReserveRecord, error)

// GetReservesInRange gets the reserve records for a range of time given a currency
// creator mint and interval. The start and end timestamps are provided along with
// the interval.
//
// ErrNotFound is returned if the mint or the reserves for the mint cannot be found
// ErrInvalidRange is returned if the range is not valid
// ErrInvalidInterval is returned if the interval is not valid
GetReservesInRange(ctx context.Context, mint string, interval query.Interval, start time.Time, end time.Time, ordering query.Ordering) ([]*ReserveRecord, error)
}
81 changes: 81 additions & 0 deletions ocp/data/currency/tests/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func RunTests(t *testing.T, s currency.Store, teardown func()) {
testGetExchangeRatesInRange,
testMetadataRoundTrip,
testReserveRoundTrip,
testGetReservesInRange,
} {
tf(t, s)
teardown()
Expand Down Expand Up @@ -100,12 +101,31 @@ func testGetExchangeRatesInRange(t *testing.T, s currency.Store) {
result, err := s.GetExchangeRatesInRange(context.Background(), "usd", query.IntervalRaw, rates[0].Time, rates[99].Time, query.Ascending)
require.NoError(t, err)
assert.Equal(t, len(result), 100)
for i, item := range result {
assert.Equal(t, rates[i].Time.Unix(), item.Time.Unix())
assert.EqualValues(t, rates[i].Rates["usd"], item.Rate)
}

result, err = s.GetExchangeRatesInRange(context.Background(), "usd", query.IntervalRaw, rates[0].Time, rates[49].Time, query.Ascending)
require.NoError(t, err)
assert.Equal(t, len(result), 50)
for i, item := range result {
assert.Equal(t, rates[i].Time.Unix(), item.Time.Unix())
assert.EqualValues(t, rates[i].Rates["usd"], item.Rate)
}

result, err = s.GetExchangeRatesInRange(context.Background(), "usd", query.IntervalRaw, rates[0].Time, rates[99].Time, query.Descending)
require.NoError(t, err)
assert.Equal(t, len(result), 100)
for i, item := range result {
assert.Equal(t, rates[99-i].Time.Unix(), item.Time.Unix())
assert.EqualValues(t, rates[99-i].Rates["usd"], item.Rate)
}

_, err = s.GetExchangeRatesInRange(context.Background(), "usd", query.IntervalSecond, rates[0].Time, rates[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetExchangeRatesInRange(context.Background(), "usd", query.IntervalMinute, rates[0].Time, rates[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetExchangeRatesInRange(context.Background(), "usd", query.IntervalHour, rates[0].Time, rates[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetExchangeRatesInRange(context.Background(), "usd", query.IntervalDay, rates[0].Time, rates[99].Time, query.Ascending)
Expand Down Expand Up @@ -196,6 +216,67 @@ func testReserveRoundTrip(t *testing.T, s currency.Store) {
assert.Equal(t, currency.ErrNotFound, err)
}

func testGetReservesInRange(t *testing.T, s currency.Store) {
var reserves []currency.ReserveRecord

now := time.Now().UTC()
mint := "test-mint"

for i := 0; i < 100; i++ {
reserves = append(reserves, currency.ReserveRecord{
Mint: mint,
SupplyFromBonding: uint64(1000 + i),
Time: now.Add(time.Duration(i) * time.Hour),
})
}

record, err := s.GetReserveAtTime(context.Background(), mint, reserves[0].Time)
assert.Nil(t, record)
assert.Equal(t, currency.ErrNotFound, err)

for _, item := range reserves {
itemCopy := item
require.NoError(t, s.PutReserveRecord(context.Background(), &itemCopy))
}

result, err := s.GetReservesInRange(context.Background(), mint, query.IntervalRaw, reserves[0].Time, reserves[99].Time, query.Ascending)
require.NoError(t, err)
assert.Equal(t, len(result), 100)
for i, item := range result {
assert.Equal(t, reserves[i].Time.Unix(), item.Time.Unix())
assert.EqualValues(t, reserves[i].SupplyFromBonding, item.SupplyFromBonding)
}

result, err = s.GetReservesInRange(context.Background(), mint, query.IntervalRaw, reserves[0].Time, reserves[49].Time, query.Ascending)
require.NoError(t, err)
assert.Equal(t, len(result), 50)
for i, item := range result {
assert.Equal(t, reserves[i].Time.Unix(), item.Time.Unix())
assert.EqualValues(t, reserves[i].SupplyFromBonding, item.SupplyFromBonding)
}

result, err = s.GetReservesInRange(context.Background(), mint, query.IntervalRaw, reserves[0].Time, reserves[99].Time, query.Descending)
require.NoError(t, err)
assert.Equal(t, len(result), 100)
for i, item := range result {
assert.Equal(t, reserves[99-i].Time.Unix(), item.Time.Unix())
assert.EqualValues(t, reserves[99-i].SupplyFromBonding, item.SupplyFromBonding)
}

_, err = s.GetReservesInRange(context.Background(), mint, query.IntervalSecond, reserves[0].Time, reserves[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetReservesInRange(context.Background(), mint, query.IntervalMinute, reserves[0].Time, reserves[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetReservesInRange(context.Background(), mint, query.IntervalHour, reserves[0].Time, reserves[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetReservesInRange(context.Background(), mint, query.IntervalDay, reserves[0].Time, reserves[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetReservesInRange(context.Background(), mint, query.IntervalWeek, reserves[0].Time, reserves[99].Time, query.Ascending)
require.NoError(t, err)
_, err = s.GetReservesInRange(context.Background(), mint, query.IntervalMonth, reserves[0].Time, reserves[99].Time, query.Ascending)
require.NoError(t, err)
}

func assertEquivalentMetadataRecords(t *testing.T, obj1, obj2 *currency.MetadataRecord) {
assert.Equal(t, obj1.Name, obj2.Name)
assert.Equal(t, obj1.Symbol, obj2.Symbol)
Expand Down
19 changes: 19 additions & 0 deletions ocp/data/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ type DatabaseData interface {
GetCurrencyMetadata(ctx context.Context, mint string) (*currency.MetadataRecord, error)
PutCurrencyReserve(ctx context.Context, record *currency.ReserveRecord) error
GetCurrencyReserveAtTime(ctx context.Context, mint string, t time.Time) (*currency.ReserveRecord, error)
GetCurrencyReserveHistory(ctx context.Context, mint string, opts ...query.Option) ([]*currency.ReserveRecord, error)

// Deposits
// --------------------------------------------------------------------------------
Expand Down Expand Up @@ -515,6 +516,24 @@ func (dp *DatabaseProvider) PutCurrencyReserve(ctx context.Context, record *curr
func (dp *DatabaseProvider) GetCurrencyReserveAtTime(ctx context.Context, mint string, t time.Time) (*currency.ReserveRecord, error) {
return dp.currencies.GetReserveAtTime(ctx, mint, t)
}
func (dp *DatabaseProvider) GetCurrencyReserveHistory(ctx context.Context, mint string, opts ...query.Option) ([]*currency.ReserveRecord, error) {
req := query.QueryOptions{
Limit: maxCurrencyHistoryReqSize,
End: time.Now(),
SortBy: query.Ascending,
Supported: query.CanLimitResults | query.CanSortBy | query.CanBucketBy | query.CanQueryByStartTime | query.CanQueryByEndTime,
}
req.Apply(opts...)

if req.Start.IsZero() {
return nil, query.ErrQueryNotSupported
}
if req.Limit > maxCurrencyHistoryReqSize {
return nil, query.ErrQueryNotSupported
}

return dp.currencies.GetReservesInRange(ctx, mint, req.Interval, req.Start, req.End, req.SortBy)
}

// Deposits
// --------------------------------------------------------------------------------
Expand Down
Loading