From 75546902d41e4dfec0adcb579ee1b9207a0a3252 Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Tue, 29 Sep 2026 16:58:20 +0200 Subject: [PATCH 01/14] wip --- marketplace/db/schema.sql | 44 +++++++++-- marketplace/db/sqlite.go | 140 +++++++++++++++++++++++++++++++++ marketplace/storage/storage.go | 44 ++++++++++- 3 files changed, 220 insertions(+), 8 deletions(-) diff --git a/marketplace/db/schema.sql b/marketplace/db/schema.sql index a971c9d063..b2fb4b5575 100644 --- a/marketplace/db/schema.sql +++ b/marketplace/db/schema.sql @@ -3,24 +3,54 @@ CREATE TABLE IF NOT EXISTS Assets( account_id INTEGER, isd_id INTEGER NOT NULL, as_id INTEGER NOT NULL, - bandwidth INTEGER NOT NULL, + bandwidth INTEGER NOT NULL CHECK (bandwidth > 0), bandwidth_min INTEGER NOT NULL, bandwidth_max INTEGER NOT NULL CHECK (bandwidth_max >= bandwidth_min), price INTEGER NOT NULL, time_granularity INTEGER NOT NULL, time_min_duration INTEGER NOT NULL, time_max_duration INTEGER NOT NULL, - starts_at TEXT NOT NULL, - stops_at TEXT NOT NULL, + starts_at INTEGER NOT NULL, + stops_at INTEGER NOT NULL, ingress INTEGER, egress INTEGER, state INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (account_id) REFERENCES Accounts(id) ); -CREATE INDEX IF NOT EXISTS idx_asset_owner ON Assets(account_id); -CREATE INDEX IF NOT EXISTS idx_asset_validity ON Assets(starts_at, stops_at); -CREATE INDEX IF NOT EXISTS idx_assets_ia_validity_start ON Assets(isd_id, as_id, starts_at, stops_at); -CREATE INDEX IF NOT EXISTS idx_assets_ia_validity_end ON Assets(isd_id, as_id, stops_at, starts_at); +CREATE INDEX IF NOT EXISTS idx_asset_owner ON Assets(account_id, isd_id, as_id); +CREATE INDEX IF NOT EXISTS idx_assets_validity ON Assets(isd_id, as_id, stops_at); + +CREATE TABLE IF NOT EXISTS Asset_Segment( + asset_id INTEGER NOT NULL, + starts_at INTEGER NOT NULL, + stops_at INTEGER NOT NULL, + available INTEGER NOT NULL CHECK (available >= 0), + PRIMARY KEY (asset_id, starts_at), + CHECK (starts_at < stops_at), + FOREIGN KEY(asset_id) REFERENCES Assets(id) ON DELETE CASCADE +) WITHOUT ROWID; + +CREATE TABLE IF NOT EXISTS Purchase_Order( + id INTEGER PRIMARY KEY, + account_id INTEGER NOT NULL, + created_at INTEGER NOT NULL DEFAULT (unixepoch()), + FOREIGN KEY(buyer_id) REFERENCES Accounts(id) +); + +CREATE TABLE IF NOT EXISTS Purchase( + id INTEGER PRIMARY KEY, + order_id INTEGER NOT NULL, + asset_id INTEGER NOT NULL, + bandwidth INTEGER NOT NULL CHECK (bandwidth > 0), + starts_at INTEGER NOT NULL, + stops_at INTEGER NOT NULL, + FOREIGN KEY(order_id) REFERENCES Purchase_Order(id), + FOREIGN KEY(asset_id) REFERENCES Assets(id), + CHECK (starts_at < stops_at) +); + +CREATE INDEX idx_purchase_asset ON Purchase(asset_id); + CREATE TABLE IF NOT EXISTS Reservations( id INTEGER PRIMARY KEY AUTOINCREMENT, reservation_id INTEGER NOT NULL, diff --git a/marketplace/db/sqlite.go b/marketplace/db/sqlite.go index ee292d9903..d342f85883 100644 --- a/marketplace/db/sqlite.go +++ b/marketplace/db/sqlite.go @@ -64,6 +64,9 @@ type Repository interface { RemoveAsset(ctx context.Context, assetID int64) error RegisterAssetEvent(ctx context.Context, a *DBAsset, eventType AssetEventType) (int64, error) TransitionAsset(ctx context.Context, assetID int64, accountID *int64, from AssetState, to AssetState) (*DBAsset, error) + InsertAssetSegment(ctx context.Context, asset_id int64, from int64, to int64, bw uint32) (int64, error) + NewOrder(ctx context.Context, accountId int64) (int64, error) + BuyAsset(ctx context.Context, assetId int64, startsAt int64, stopsAt int64, bw uint32, accountId int64, orderId int64) (int64, error) } type MarketplaceDB interface { @@ -1147,3 +1150,140 @@ func (e *executor) AssignReservation( } return res.RowsAffected() } + +func (e *executor) InsertAssetSegment(ctx context.Context, asset_id int64, from int64, to int64, bw uint32) (int64, error) { + if e.write == nil { + return 0, serrors.New("No database open") + } + inst := `INSERT INTO Asset_Segment(asset_id, starts_at, stops_at, available) VALUES (?,?,?,?)` + res, err := e.write.ExecContext(ctx, inst, asset_id, from, to, bw) + if err != nil { + return 0, err + } + return res.LastInsertId() +} + +func (e *executor) NewOrder( + ctx context.Context, + accountId int64, +) (int64, error) { + if e.write == nil { + return 0, serrors.New("No database open") + } + inst := `INSERT INTO purchase_order (account_id) VALUES (?) RETURNING id` + var orderId int64 + err := e.write.QueryRowContext(ctx, inst, accountId).Scan(&orderId) + if err != nil { + return 0, err + } + return orderId, nil +} + +// Buys a single asset. Must be called as part of a transaction +func (e *executor) BuyAsset( + ctx context.Context, + assetId int64, + startsAt int64, + stopsAt int64, + bw uint32, + accountId int64, + orderId int64, +) (int64, error) { + if e.write == nil { + return 0, serrors.New("No database open") + } + splitAt := func(point int64) error { + insertInst := ` + INSERT INTO Asset_segment (asset_id, starts_at, stops_at, available) + SELECT asset_id, ?, stops_at, available + FROM Asset_segment + WHERE asset_id = ? AND starts_at < ? AND stops_at > ?` + _, err := e.write.ExecContext(ctx, insertInst, point, assetId, point, point) + if err != nil { + return err + } + updateInst := ` + UPDATE Asset_segment + SET stops_at = ? + WHERE asset_id = ? AND starts_at < ? AND stops_at > ? + ` + _, err = e.write.ExecContext(ctx, updateInst, point, assetId, point, point) + if err != nil { + return err + } + return nil + } + mergeAt := func(point int64) error { + updateInst := ` + UPDATE Asset_Segment AS l + SET stops_at = r.stops_at + FROM Asset_Segment AS r + WHERE l.asset_id = ? AND l.stops_at = ? + AND r.asset_id = ? AND r.starts_at = ? + AND r.available = l.available + ` + res, err := e.write.ExecContext(ctx, updateInst, assetId, point, assetId, point) + if err != nil { + return fmt.Errorf("merge at %d (update): %w", point, err) + } + if n, _ := res.RowsAffected(); n == 1 { + deleteInst := `DELETE FROM Asset_Segment WHERE asset_id = ? AND starts_at = ?` + if _, err := e.write.ExecContext(ctx, deleteInst, assetId, point); err != nil { + return fmt.Errorf("merge at %d (delete): %w", point, err) + } + } + return nil + } + var id int64 + inst := ` + SELECT a.id + FROM Assets a + WHERE a.id = ? + AND a.starts_at <= ? AND a.stops_at >= ? + AND NOT EXISTS ( + SELECT 1 FROM Asset_Segment g + WHERE g.asset_id = a.id + AND g.starts_at < ? AND g.stops_at > ? + AND g.available < ?); + ` + err := e.write.QueryRowContext(ctx, inst, assetId, startsAt, stopsAt, stopsAt, startsAt).Scan(&id) + if err != nil { + return 0, err + } + + err = splitAt(startsAt) + if err != nil { + return 0, err + } + err = splitAt(stopsAt) + if err != nil { + return 0, err + } + decreaseAvailabilityInst := ` + UPDATE asset_segment + SET available = available - ? + WHERE asset_id = ? AND starts_at >= ? AND stops_at <= ?` + _, err = e.write.ExecContext(ctx, decreaseAvailabilityInst, bw, assetId, startsAt, stopsAt) + if err != nil { + return 0, err + } + // TODO: we still need a new asset ID for the now user-owned asset. Maybe include it here? + recordPurchaseInst := ` + INSERT INTO purchase (order_id, asset_id, bandwidth, starts_at, stops_at) + VALUES (?, ?, ?, ?, ?) + ` + _, err = e.write.ExecContext(ctx, recordPurchaseInst, orderId, assetId, bw, startsAt, stopsAt) + if err != nil { + return 0, err + } + err = mergeAt(startsAt) + if err != nil { + return 0, err + } + err = mergeAt(stopsAt) + if err != nil { + return 0, err + } + // TODO return price + return 0, nil +} diff --git a/marketplace/storage/storage.go b/marketplace/storage/storage.go index a5ac35939c..6af90b0414 100644 --- a/marketplace/storage/storage.go +++ b/marketplace/storage/storage.go @@ -98,7 +98,11 @@ func (s *MarketplaceStorage) PublishAsset( if err != nil { return err } - _, err := tx.RegisterAssetEvent(ctx, a, marketplacedb.AssetPublished) + _, err = tx.InsertAssetSegment(ctx, assetId, a.StartAt.Unix(), a.StopsAt.Unix(), a.Bandwidth) + if err != nil { + return err + } + _, err = tx.RegisterAssetEvent(ctx, a, marketplacedb.AssetPublished) if err != nil { return err } @@ -602,6 +606,44 @@ func (s *MarketplaceStorage) BuyAssets( accountID int64, assets []*hummingbird.BuyAsset, maxPrice uint64, +) ([]int64, int64, error) { + if len(assets) == 0 { + return nil, 0, serrors.New("no assets specified") + } + for _, asset := range assets { + if asset.BandwidthExact <= 0 || asset.StartsAtExactly.Seconds >= asset.StopsAtExactly.Seconds { + return nil, 0, serrors.New("invalid asset") + } + } + err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { + orderId, err := tx.NewOrder(ctx, accountID) + if err != nil { + return err + } + for _, asset := range assets { + assetId, err := DatabaseAssetID(asset.AssetId) + if err != nil { + return err + } + price, err := tx.BuyAsset(ctx, assetId, asset.StartsAtExactly.Seconds, asset.StopsAtExactly.Seconds, + asset.BandwidthExact, accountID, orderId) + if err != nil { + return err + } + } + return nil + }) + if err != nil { + return nil, 0, err + } + return nil, 0, nil +} + +func (s *MarketplaceStorage) BuyAssetsOld( + ctx context.Context, + accountID int64, + assets []*hummingbird.BuyAsset, + maxPrice uint64, ) ([]int64, int64, error) { uniqueCheck := make(map[int64]bool) for _, asset := range assets { From 8766ffedc36d2cca5ba25eb271e6abba3a2b2616 Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Fri, 2 Oct 2026 11:58:08 +0200 Subject: [PATCH 02/14] updated buy assets implementation --- marketplace/db/schema.sql | 21 ------ marketplace/db/sqlite.go | 105 ++++++++++++++++-------------- marketplace/db/sqlite_test.go | 6 +- marketplace/db/types.go | 4 +- marketplace/service.go | 10 ++- marketplace/storage/storage.go | 61 ++++++++++++++++-- tools/topology/marketplace.py | 113 +++++++++++++++++++++++++-------- 7 files changed, 205 insertions(+), 115 deletions(-) diff --git a/marketplace/db/schema.sql b/marketplace/db/schema.sql index b2fb4b5575..652f1ba370 100644 --- a/marketplace/db/schema.sql +++ b/marketplace/db/schema.sql @@ -30,27 +30,6 @@ CREATE TABLE IF NOT EXISTS Asset_Segment( FOREIGN KEY(asset_id) REFERENCES Assets(id) ON DELETE CASCADE ) WITHOUT ROWID; -CREATE TABLE IF NOT EXISTS Purchase_Order( - id INTEGER PRIMARY KEY, - account_id INTEGER NOT NULL, - created_at INTEGER NOT NULL DEFAULT (unixepoch()), - FOREIGN KEY(buyer_id) REFERENCES Accounts(id) -); - -CREATE TABLE IF NOT EXISTS Purchase( - id INTEGER PRIMARY KEY, - order_id INTEGER NOT NULL, - asset_id INTEGER NOT NULL, - bandwidth INTEGER NOT NULL CHECK (bandwidth > 0), - starts_at INTEGER NOT NULL, - stops_at INTEGER NOT NULL, - FOREIGN KEY(order_id) REFERENCES Purchase_Order(id), - FOREIGN KEY(asset_id) REFERENCES Assets(id), - CHECK (starts_at < stops_at) -); - -CREATE INDEX idx_purchase_asset ON Purchase(asset_id); - CREATE TABLE IF NOT EXISTS Reservations( id INTEGER PRIMARY KEY AUTOINCREMENT, reservation_id INTEGER NOT NULL, diff --git a/marketplace/db/sqlite.go b/marketplace/db/sqlite.go index d342f85883..8d9d564c26 100644 --- a/marketplace/db/sqlite.go +++ b/marketplace/db/sqlite.go @@ -65,8 +65,7 @@ type Repository interface { RegisterAssetEvent(ctx context.Context, a *DBAsset, eventType AssetEventType) (int64, error) TransitionAsset(ctx context.Context, assetID int64, accountID *int64, from AssetState, to AssetState) (*DBAsset, error) InsertAssetSegment(ctx context.Context, asset_id int64, from int64, to int64, bw uint32) (int64, error) - NewOrder(ctx context.Context, accountId int64) (int64, error) - BuyAsset(ctx context.Context, assetId int64, startsAt int64, stopsAt int64, bw uint32, accountId int64, orderId int64) (int64, error) + BuyAsset(ctx context.Context, assetId int64, startsAt int64, stopsAt int64, bw uint32, accountId int64) (*DBAsset, error) } type MarketplaceDB interface { @@ -157,8 +156,8 @@ type rowScanner interface { func scanAsset(row rowScanner) (*DBAsset, error) { asset := &DBAsset{} - var startsAt string - var stopsAt string + var startsAt int64 + var stopsAt int64 var isd uint16 var as uint64 if err := row.Scan( @@ -185,14 +184,8 @@ func scanAsset(row rowScanner) (*DBAsset, error) { if err != nil { return nil, err } - asset.StartAt, err = time.Parse(time.RFC3339, startsAt) - if err != nil { - return nil, err - } - asset.StopsAt, err = time.Parse(time.RFC3339, stopsAt) - if err != nil { - return nil, err - } + asset.StartAt = time.Unix(startsAt, 0) + asset.StopsAt = time.Unix(stopsAt, 0) return asset, nil } @@ -800,8 +793,8 @@ func (e *executor) InsertAsset(ctx context.Context, a *DBAsset) (int64, error) { a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, - a.StartAt.UTC().Format(time.RFC3339), - a.StopsAt.UTC().Format(time.RFC3339), + a.StartAt.Unix(), + a.StopsAt.Unix(), a.IfIdIngress, a.IfIdEgress, a.AccountId) @@ -818,8 +811,8 @@ func (e *executor) InsertAsset(ctx context.Context, a *DBAsset) (int64, error) { a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, - a.StartAt.UTC().Format(time.RFC3339), - a.StopsAt.UTC().Format(time.RFC3339), + a.StartAt.Unix(), + a.StopsAt.Unix(), a.IfIdIngress, a.IfIdEgress) } @@ -1163,22 +1156,6 @@ func (e *executor) InsertAssetSegment(ctx context.Context, asset_id int64, from return res.LastInsertId() } -func (e *executor) NewOrder( - ctx context.Context, - accountId int64, -) (int64, error) { - if e.write == nil { - return 0, serrors.New("No database open") - } - inst := `INSERT INTO purchase_order (account_id) VALUES (?) RETURNING id` - var orderId int64 - err := e.write.QueryRowContext(ctx, inst, accountId).Scan(&orderId) - if err != nil { - return 0, err - } - return orderId, nil -} - // Buys a single asset. Must be called as part of a transaction func (e *executor) BuyAsset( ctx context.Context, @@ -1187,10 +1164,9 @@ func (e *executor) BuyAsset( stopsAt int64, bw uint32, accountId int64, - orderId int64, -) (int64, error) { +) (*DBAsset, error) { if e.write == nil { - return 0, serrors.New("No database open") + return nil, serrors.New("No database open") } splitAt := func(point int64) error { insertInst := ` @@ -1234,11 +1210,11 @@ func (e *executor) BuyAsset( } return nil } - var id int64 inst := ` - SELECT a.id - FROM Assets a + SELECT ` + assetColumnsWithAlias + ` FROM Assets a WHERE a.id = ? + AND a.account_ID IS NULL + AND a.state = ? AND a.starts_at <= ? AND a.stops_at >= ? AND NOT EXISTS ( SELECT 1 FROM Asset_Segment g @@ -1246,18 +1222,27 @@ func (e *executor) BuyAsset( AND g.starts_at < ? AND g.stops_at > ? AND g.available < ?); ` - err := e.write.QueryRowContext(ctx, inst, assetId, startsAt, stopsAt, stopsAt, startsAt).Scan(&id) + fmt.Println(inst) + fmt.Println(assetId, AssetStateAvailable, startsAt, stopsAt, stopsAt, startsAt, bw) + rows, err := e.write.QueryContext(ctx, inst, assetId, AssetStateAvailable, startsAt, stopsAt, stopsAt, startsAt, bw) if err != nil { - return 0, err + return nil, err + } + if !rows.Next() { + return nil, serrors.New("asset not found") + } + asset, err := scanAsset(rows) + if err != nil { + return nil, err } err = splitAt(startsAt) if err != nil { - return 0, err + return nil, err } err = splitAt(stopsAt) if err != nil { - return 0, err + return nil, err } decreaseAvailabilityInst := ` UPDATE asset_segment @@ -1265,25 +1250,47 @@ func (e *executor) BuyAsset( WHERE asset_id = ? AND starts_at >= ? AND stops_at <= ?` _, err = e.write.ExecContext(ctx, decreaseAvailabilityInst, bw, assetId, startsAt, stopsAt) if err != nil { - return 0, err + return nil, err + } + userAsset := &DBAsset{ + AccountId: sql.NullInt64{ + Int64: accountId, + Valid: true, + }, + IA: asset.IA, + Bandwidth: bw, + StartAt: time.Unix(startsAt, 0), + StopsAt: time.Unix(stopsAt, 0), + BandwidthMin: asset.BandwidthMin, + BandwidthMax: asset.BandwidthMax, + Price: asset.Price, + TimeGranularity: asset.TimeGranularity, + TimeMinDuration: asset.TimeMinDuration, + TimeMaxDuration: asset.TimeMaxDuration, + IfIdIngress: asset.IfIdIngress, + IfIdEgress: asset.IfIdEgress, + } + newId, err := e.InsertAsset(ctx, userAsset) + if err != nil { + return nil, err } + userAsset.ID = newId // TODO: we still need a new asset ID for the now user-owned asset. Maybe include it here? - recordPurchaseInst := ` + /*recordPurchaseInst := ` INSERT INTO purchase (order_id, asset_id, bandwidth, starts_at, stops_at) VALUES (?, ?, ?, ?, ?) ` _, err = e.write.ExecContext(ctx, recordPurchaseInst, orderId, assetId, bw, startsAt, stopsAt) if err != nil { return 0, err - } + }*/ err = mergeAt(startsAt) if err != nil { - return 0, err + return nil, err } err = mergeAt(stopsAt) if err != nil { - return 0, err + return nil, err } - // TODO return price - return 0, nil + return userAsset, nil } diff --git a/marketplace/db/sqlite_test.go b/marketplace/db/sqlite_test.go index 7b11494d1d..c8e6b751d6 100644 --- a/marketplace/db/sqlite_test.go +++ b/marketplace/db/sqlite_test.go @@ -214,8 +214,8 @@ func TestSearchOwnedAssets(t *testing.T) { require.Equal(t, ownedID, assets[0].ID) // The validity filters still apply to the assets of the account. - startsAt := owned.StartAt.UTC().Format(time.RFC3339) - stopsAt := owned.StopsAt.UTC().Format(time.RFC3339) + startsAt := owned.StartAt.Unix() + stopsAt := owned.StopsAt.Unix() assets, err = backend.Search(ctx, &AssetQuery{ AccountId: &alicesAccountID, StartsAt: &startsAt, @@ -225,7 +225,7 @@ func TestSearchOwnedAssets(t *testing.T) { require.NoError(t, err) require.Len(t, assets, 1) - tooLate := owned.StopsAt.Add(time.Hour).UTC().Format(time.RFC3339) + tooLate := owned.StopsAt.Add(time.Hour).Unix() assets, err = backend.Search(ctx, &AssetQuery{ AccountId: &alicesAccountID, StartsAt: &startsAt, diff --git a/marketplace/db/types.go b/marketplace/db/types.go index c74eeb7268..4cab51ec92 100644 --- a/marketplace/db/types.go +++ b/marketplace/db/types.go @@ -68,9 +68,9 @@ type AssetQuery struct { // the minimal required bandwidth of the asset MinRequiredBandwidth *uint32 // asset starts at latest in RFC3339 format - StartsAt *string + StartsAt *int64 // reservation stops at earliest in RFC3339 format - StopsAt *string + StopsAt *int64 // the price in kbps per second Price *uint32 // the page number diff --git a/marketplace/service.go b/marketplace/service.go index 9e52e5787c..53ef10bfc2 100644 --- a/marketplace/service.go +++ b/marketplace/service.go @@ -693,15 +693,13 @@ func (s *Service) SearchAssets(ctx context.Context, req *connect.Request[humming default: return nil, connect.NewError(connect.CodePermissionDenied, serrors.New("user_id not provided")) } - var startsAt *string - var stopsAt *string + var startsAt *int64 + var stopsAt *int64 if req.Msg.StartsAtLatest != nil { - start := req.Msg.StartsAtLatest.AsTime().UTC().Format(time.RFC3339) - startsAt = &start + startsAt = &req.Msg.StartsAtLatest.Seconds } if req.Msg.StopsAtEarliest != nil { - stop := req.Msg.StopsAtEarliest.AsTime().UTC().Format(time.RFC3339) - stopsAt = &stop + stopsAt = &req.Msg.StartsAtLatest.Seconds } var ia *addr.IA if req.Msg.Ia != nil { diff --git a/marketplace/storage/storage.go b/marketplace/storage/storage.go index 6af90b0414..d198b32540 100644 --- a/marketplace/storage/storage.go +++ b/marketplace/storage/storage.go @@ -342,6 +342,33 @@ func addInt64( return a + b, true } +func multiplyInts(xs ...int64) (int64, bool) { + result := int64(1) + + for _, x := range xs { + if x == 0 { + return 0, true + } + if result > 0 { + if x > 0 && result > math.MaxInt64/x { + return 0, false + } + if x < 0 && x < math.MinInt64/result { + return 0, false + } + } else { + if x > 0 && result < math.MinInt64/x { + return 0, false + } + if x < 0 && result < math.MaxInt64/x { + return 0, false + } + } + result *= x + } + return result, true +} + func (s *MarketplaceStorage) IncrementASJWTVersion( ctx context.Context, ia addr.IA, @@ -615,28 +642,50 @@ func (s *MarketplaceStorage) BuyAssets( return nil, 0, serrors.New("invalid asset") } } + boughtIDs := []int64{} + costAcc := int64(0) err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { - orderId, err := tx.NewOrder(ctx, accountID) - if err != nil { - return err + calcCost := func(a *marketplacedb.DBAsset) (int64, bool) { + totalAssetPrice, ok := multiplyInts(int64(a.StopsAt.Sub(a.StartAt).Seconds()), + int64(a.Bandwidth), int64(a.Price)) + if !ok { + return 0, false + } + fee := int64(float64(totalAssetPrice)*float64(s.transactionFeeRelative)) + + int64(s.transactionFeeAbsolute) + return addInt64(int64(totalAssetPrice), fee) } for _, asset := range assets { assetId, err := DatabaseAssetID(asset.AssetId) if err != nil { return err } - price, err := tx.BuyAsset(ctx, assetId, asset.StartsAtExactly.Seconds, asset.StopsAtExactly.Seconds, - asset.BandwidthExact, accountID, orderId) + newAsset, err := tx.BuyAsset(ctx, assetId, asset.StartsAtExactly.Seconds, asset.StopsAtExactly.Seconds, + asset.BandwidthExact, accountID) if err != nil { return err } + cost, ok := calcCost(newAsset) + if !ok { + return serrors.New("asset parameters would lead to integer overflow") + } + costAcc, ok = addInt64(costAcc, cost) + if !ok { + return serrors.New("asset parameters would lead to integer overflow") + } + + boughtIDs = append(boughtIDs, newAsset.ID) + } + _, err := tx.UpdateAccountMoney(ctx, accountID, -costAcc) + if err != nil { + return err } return nil }) if err != nil { return nil, 0, err } - return nil, 0, nil + return boughtIDs, costAcc, nil } func (s *MarketplaceStorage) BuyAssetsOld( diff --git a/tools/topology/marketplace.py b/tools/topology/marketplace.py index bd9556ed55..39a873f111 100644 --- a/tools/topology/marketplace.py +++ b/tools/topology/marketplace.py @@ -595,48 +595,105 @@ def insertASes(db, ases): ) +def rfc3339_to_unix(timestamp): + return int(datetime.fromisoformat( + timestamp.replace("Z", "+00:00") + ).timestamp()) + def insertAssets(db, assets): if assets is None: return - rows = [] + events = [] + segments = [] + for a in assets: isd_id, as_id = iaNumbers(a.get("ia")) - rows.append(( - isd_id, as_id, a.get("bandwidth"), a.get("bandwidth_min"), a.get("bandwidth_max"), - a.get("price"), a.get("time_granularity"), a.get("time_min_duration"), - a.get("time_max_duration"), a.get("starts_at"), a.get("stops_at"), - a.get("ingress"), a.get("egress"), a.get("owner"), + cursor = db.execute( + """ + INSERT INTO Assets ( + isd_id, as_id, bandwidth, bandwidth_min, bandwidth_max, price, + time_granularity, time_min_duration, time_max_duration, starts_at, + stops_at, ingress, egress, account_id + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ( + SELECT a.id + FROM Accounts a + JOIN Users u ON u.ID = a.user_id + WHERE u.name = ? AND a.scope = '' + LIMIT 1 + )) + RETURNING id + """, + ( + isd_id, + as_id, + a.get("bandwidth"), + a.get("bandwidth_min"), + a.get("bandwidth_max"), + a.get("price"), + a.get("time_granularity"), + a.get("time_min_duration"), + a.get("time_max_duration"), + rfc3339_to_unix(a.get("starts_at")), + rfc3339_to_unix(a.get("stops_at")), + a.get("ingress"), + a.get("egress"), + a.get("owner"), + ), + ) + + asset_id = cursor.fetchone()[0] + + # The segment covers the whole lifetime of the asset. + segments.append(( + asset_id, + rfc3339_to_unix(a.get("starts_at")), + rfc3339_to_unix(a.get("stops_at")), + a.get("bandwidth"), )) - # The statistics of an AS are computed from the events, - # not from the assets still on sale, so publishing one has to be recorded as well. - # An asset that already has an owner means it was bought, which is a second event. - events.append((AssetEvent.PUBLISHED, isd_id, as_id, a.get("ingress"), - a.get("egress"), a.get("bandwidth"), a.get("starts_at"), - a.get("stops_at"), a.get("price"))) + + events.append(( + AssetEvent.PUBLISHED, + isd_id, + as_id, + a.get("ingress"), + a.get("egress"), + a.get("bandwidth"), + a.get("starts_at"), + a.get("stops_at"), + a.get("price"), + )) + if a.get("owner") is not None: - events.append((AssetEvent.BOUGHT, isd_id, as_id, a.get("ingress"), - a.get("egress"), a.get("bandwidth"), a.get("starts_at"), - a.get("stops_at"), a.get("price"))) + events.append(( + AssetEvent.BOUGHT, + isd_id, + as_id, + a.get("ingress"), + a.get("egress"), + a.get("bandwidth"), + a.get("starts_at"), + a.get("stops_at"), + a.get("price"), + )) + db.executemany( """ - INSERT INTO Assets (isd_id, as_id, bandwidth, bandwidth_min, bandwidth_max, price, - time_granularity, time_min_duration, time_max_duration, starts_at, - stops_at, ingress, egress, account_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ( - SELECT a.id - FROM Accounts a - JOIN Users u ON u.ID = a.user_id - WHERE u.name = ? AND a.scope = '' - LIMIT 1 - )) + INSERT INTO Asset_Segment ( + asset_id, starts_at, stops_at, available + ) + VALUES (?, ?, ?, ?) """, - rows, + segments, ) + db.executemany( """ - INSERT INTO Asset_Events (event_type, isd_id, as_id, ingress, egress, bandwidth, - starts_at, stops_at, price) + INSERT INTO Asset_Events ( + event_type, isd_id, as_id, ingress, egress, bandwidth, + starts_at, stops_at, price + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, events, From 251874de1ed954ebe069d6d42cce78e37e029a78 Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:53:32 +0200 Subject: [PATCH 03/14] wip --- marketplace/db/sqlite.go | 193 +++++++- marketplace/db/types.go | 41 +- marketplace/service.go | 64 ++- marketplace/storage/storage.go | 13 +- marketplace/webapp/web.go | 4 +- marketplace_client/main.go | 141 +++--- pkg/hummingbird/marketplace/client.go | 89 ++-- pkg/proto/hummingbird/marketplace.pb.go | 590 ++++++++++++++++-------- proto/hummingbird/v1/marketplace.proto | 37 +- 9 files changed, 821 insertions(+), 351 deletions(-) diff --git a/marketplace/db/sqlite.go b/marketplace/db/sqlite.go index 8d9d564c26..921cfdd171 100644 --- a/marketplace/db/sqlite.go +++ b/marketplace/db/sqlite.go @@ -33,7 +33,8 @@ import ( type Repository interface { InsertAsset(ctx context.Context, a *DBAsset) (int64, error) InsertReservation(ctx context.Context, r *DBReservation) (int64, error) - Search(ctx context.Context, params *AssetQuery) ([]*DBAsset, error) + SearchOwnedAssets(ctx context.Context, params *OwnedAssetQuery) ([]*DBAsset, error) + SearchListedAssets(ctx context.Context, params *ListedAssetQuery) ([]*DBAsset, error) FetchReservations(ctx context.Context, params *ReservationQuery) ([]*DBReservation, error) GetUser(ctx context.Context, id int64) (*DBUser, error) GetUserByName(ctx context.Context, name string) (*DBUser, error) @@ -154,6 +155,44 @@ type rowScanner interface { Scan(dest ...any) error } +func scanAssetWithAvailableBandwidth(row rowScanner) (*DBAsset, error) { + asset := &DBAsset{} + var startsAt int64 + var stopsAt int64 + var isd uint16 + var as uint64 + var availableBW uint32 + if err := row.Scan( + &asset.ID, + &asset.AccountId, + &isd, + &as, + &asset.Bandwidth, + &asset.BandwidthMin, + &asset.BandwidthMax, + &asset.Price, + &asset.TimeGranularity, + &asset.TimeMinDuration, + &asset.TimeMaxDuration, + &startsAt, + &stopsAt, + &asset.IfIdIngress, + &asset.IfIdEgress, + &availableBW, + ); err != nil { + return nil, serrors.Wrap("Error reading DB response", err) + } + var err error + asset.IA, err = addr.IAFrom(addr.ISD(isd), addr.AS(as)) + if err != nil { + return nil, err + } + asset.StartAt = time.Unix(startsAt, 0) + asset.StopsAt = time.Unix(stopsAt, 0) + asset.Bandwidth = availableBW + return asset, nil +} + func scanAsset(row rowScanner) (*DBAsset, error) { asset := &DBAsset{} var startsAt int64 @@ -637,11 +676,19 @@ func (e *executor) RegisterAssetEvent(ctx context.Context, a *DBAsset, eventType return res.LastInsertId() } -func (e *executor) Search(ctx context.Context, params *AssetQuery) ([]*DBAsset, error) { +func (e *executor) SearchOwnedAssets(ctx context.Context, params *OwnedAssetQuery) ([]*DBAsset, error) { if e.read == nil { return nil, serrors.New("No database open") } - stmt, args := e.buildSearchQuery(params) + var stmt string + var args []any + if params.ValidityMode == FullOverlap { + stmt, args = e.buildSearchOwnedAssetsFullOverlapQuery(params) + } else if params.ValidityMode == PartialOverlap { + stmt, args = e.buildSearchOwnedAssetsPartialOverlapQuery(params) + } else { + return nil, serrors.New("invalid validity mode") + } rows, err := e.read.QueryContext(ctx, stmt, args...) if err != nil { return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) @@ -658,23 +705,138 @@ func (e *executor) Search(ctx context.Context, params *AssetQuery) ([]*DBAsset, return res, nil } -func (e *executor) buildSearchQuery(params *AssetQuery) (string, []any) { +func (e *executor) SearchListedAssets(ctx context.Context, params *ListedAssetQuery) ([]*DBAsset, error) { + if e.read == nil { + return nil, serrors.New("No database open") + } + var stmt string + var args []any + if params.ValidityMode == FullOverlap { + stmt, args = e.buildSearchListedAssetsFullOverlapQuery(params) + fmt.Println(stmt, args) + } else if params.ValidityMode == PartialOverlap { + stmt, args = e.buildSearchListedAssetsPartialOverlapQuery(params) + } else { + return nil, serrors.New("invalid validity mode") + } + rows, err := e.read.QueryContext(ctx, stmt, args...) + if err != nil { + return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) + } + defer rows.Close() + var res []*DBAsset + for rows.Next() { + a, err := scanAssetWithAvailableBandwidth(rows) + if err != nil { + return nil, err + } + res = append(res, a) + } + return res, nil +} + +func (e *executor) buildSearchListedAssetsPartialOverlapQuery(params *ListedAssetQuery) (string, []any) { + return "", nil +} + +func (e *executor) buildSearchListedAssetsFullOverlapQuery(params *ListedAssetQuery) (string, []any) { + var args []any + where := []string{} + queryStart := []string{` + + SELECT ` + assetColumnsWithAlias + `, MIN(g.available) AS max_buyable_bandwidth + FROM Assets a + JOIN Asset_Segment g + ON g.asset_id = a.id AND g.starts_at < ? AND g.stops_at > ? + WHERE a.isd_id = ? AND a.as_id = ? + AND a.starts_at <= ? AND a.stops_at >= ? + AND a.account_id IS NULL + AND a.bandwidth >= ? + `, + } + queryEnd := ` + GROUP BY a.id + HAVING MIN(g.available) >= ? + ORDER BY a.id + ` + args = append(args, params.StopsAt, params.StartsAt, params.IA.ISD(), params.IA.AS(), params.StartsAt, + params.StopsAt, params.MinRequiredBandwidth) + + where = append(where, "(a.state = ?)") + args = append(args, AssetStateAvailable) + if params.Price != nil { + where = append(where, "(a.price<=?)") + args = append(args, *params.Price) + } + if params.Ingress != nil { + where = append(where, "(a.ingress=?)") + args = append(args, *params.Ingress) + } + if params.Egress != nil { + where = append(where, "(a.egress=?)") + args = append(args, *params.Egress) + } + args = append(args, params.MinRequiredBandwidth) + query := append(queryStart, fmt.Sprintf("AND %s", strings.Join(where, "AND\n")), queryEnd) + return strings.Join(query, "\n"), args +} + +func (e *executor) buildSearchOwnedAssetsPartialOverlapQuery(params *OwnedAssetQuery) (string, []any) { var args []any where := []string{} query := []string{ "SELECT " + assetColumnsWithAlias + " FROM Assets a", } - if params.AccountId == nil { - where = append(where, "(a.account_id IS NULL)") - } else { - // The JOIN clause will be the first parametrized entry. Fill up the arguments for it. - query = append(query, "JOIN Accounts owner ON a.account_id = owner.id JOIN "+ - "Accounts current ON current.id = ?") - args = append(args, *params.AccountId) - // Parenthesized as a whole: AND to all where[i] binds tighter than OR. - where = append(where, "( (current.scope = '' AND owner.user_id = current.user_id) OR "+ - "(current.scope != '' AND owner.id = current.id) )") + query = append(query, "JOIN Accounts owner ON a.account_id = owner.id JOIN "+ + "Accounts current ON current.id = ?") + args = append(args, *params.AccountId) + where = append(where, "((current.scope = '' AND owner.user_id = current.user_id) OR "+ + "(current.scope != '' AND owner.id = current.id))") + where = append(where, "(a.state = ?)") + args = append(args, AssetStateAvailable) + if params.IA != nil { + where = append(where, "(a.isd_id=?) AND (a.as_id=?)") + args = append(args, int64(params.IA.ISD()), int64(params.IA.AS())) + } + if params.StartsAt != nil { + where = append(where, "(a.starts_at>=?)") + args = append(args, *params.StartsAt) + } + if params.StopsAt != nil { + where = append(where, "(a.stops_at<=?)") + args = append(args, *params.StopsAt) + } + if params.Price != nil { + where = append(where, "(a.price<=?)") + args = append(args, *params.Price) + } + if params.MinRequiredBandwidth != nil { + where = append(where, "(a.bandwidth>=?)") + args = append(args, *params.MinRequiredBandwidth) + } + if params.Ingress != nil { + where = append(where, "(a.ingress=?)") + args = append(args, *params.Ingress) + } + if params.Egress != nil { + where = append(where, "(a.egress=?)") + args = append(args, *params.Egress) } + query = append(query, fmt.Sprintf("WHERE %s", strings.Join(where, "AND\n"))) + return strings.Join(query, "\n"), args +} + +func (e *executor) buildSearchOwnedAssetsFullOverlapQuery(params *OwnedAssetQuery) (string, []any) { + var args []any + where := []string{} + query := []string{ + "SELECT " + assetColumnsWithAlias + " FROM Assets a", + } + query = append(query, "JOIN Accounts owner ON a.account_id = owner.id JOIN "+ + "Accounts current ON current.id = ?") + args = append(args, *params.AccountId) + where = append(where, "((current.scope = '' AND owner.user_id = current.user_id) OR "+ + "(current.scope != '' AND owner.id = current.id))") where = append(where, "(a.state = ?)") args = append(args, AssetStateAvailable) if params.IA != nil { @@ -706,9 +868,6 @@ func (e *executor) buildSearchQuery(params *AssetQuery) (string, []any) { args = append(args, *params.Egress) } query = append(query, fmt.Sprintf("WHERE %s", strings.Join(where, "AND\n"))) - query = append(query, "ORDER BY LENGTH(a.id) ASC, a.id ASC") - query = append(query, "LIMIT ? OFFSET ?") - args = append(args, params.PageSize, params.Page*params.PageSize) return strings.Join(query, "\n"), args } diff --git a/marketplace/db/types.go b/marketplace/db/types.go index 4cab51ec92..f51574401b 100644 --- a/marketplace/db/types.go +++ b/marketplace/db/types.go @@ -35,6 +35,13 @@ const ( AssetStateCombinePending ) +type ValidityMode int + +const ( + FullOverlap ValidityMode = 0 + PartialOverlap = 1 +) + type AssetEventType int const ( @@ -55,8 +62,25 @@ func (id AssetID) Int64() (int64, error) { return int64(id), nil } -// Defines the query parameters for search assets -type AssetQuery struct { +type ListedAssetQuery struct { + // ISD-AS of the asset + IA addr.IA + // ingress ID of the asset + Ingress *uint32 + // egress ID of the asset + Egress *uint32 + // the minimal required bandwidth of the asset + MinRequiredBandwidth uint32 + // starts at in unix seconds + StartsAt int64 + // stops at in unix seconds + StopsAt int64 + // the maximum price in kbps per second + Price *uint32 + ValidityMode ValidityMode +} + +type OwnedAssetQuery struct { // the account ID of the owner AccountId *int64 // ISD-AS of the asset @@ -67,16 +91,13 @@ type AssetQuery struct { Egress *uint32 // the minimal required bandwidth of the asset MinRequiredBandwidth *uint32 - // asset starts at latest in RFC3339 format + // starts at in unix seconds StartsAt *int64 - // reservation stops at earliest in RFC3339 format + // stops at in unix seconds StopsAt *int64 - // the price in kbps per second - Price *uint32 - // the page number - Page uint32 - // maximum number of returned elements - PageSize uint32 + // the maximum price in kbps per second + Price *uint32 + ValidityMode ValidityMode } type UsedReservationsQuery struct { diff --git a/marketplace/service.go b/marketplace/service.go index 53ef10bfc2..93372beb22 100644 --- a/marketplace/service.go +++ b/marketplace/service.go @@ -686,46 +686,40 @@ func (s *Service) SearchAssets(ctx context.Context, req *connect.Request[humming var owner_id *int64 switch user := ctx.Value("user").(type) { case int64: - if req.Msg.Owned { - owner_id = &user - } + owner_id = &user case addr.IA: default: return nil, connect.NewError(connect.CodePermissionDenied, serrors.New("user_id not provided")) } - var startsAt *int64 - var stopsAt *int64 - if req.Msg.StartsAtLatest != nil { - startsAt = &req.Msg.StartsAtLatest.Seconds - } - if req.Msg.StopsAtEarliest != nil { - stopsAt = &req.Msg.StartsAtLatest.Seconds - } - var ia *addr.IA - if req.Msg.Ia != nil { - tmp := addr.IA(*req.Msg.Ia) - ia = &tmp - } - pageSize := s.info.MaxReturnedAssets - if pageSize == 0 { - return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("page size cannot be 0")) - } - if req.Msg.MaxReturnedAssets != nil { - pageSize = min(pageSize, *req.Msg.MaxReturnedAssets) + var assets []*db.DBAsset + var err error + switch t := req.Msg.Request.(type) { + case *hummingbird.SearchAssetsRequest_Listed: + assets, err = s.store.SearchListedAssets(ctx, &db.ListedAssetQuery{ + IA: addr.IA(t.Listed.Ia), + Ingress: t.Listed.IfIdIngress, + Egress: t.Listed.IfIdEgress, + MinRequiredBandwidth: t.Listed.MinRequiredBw, + Price: t.Listed.Price, + StartsAt: t.Listed.StartsAtLatest, + StopsAt: t.Listed.StopsAtEarliest, + ValidityMode: db.ValidityMode(req.Msg.ValidityMode), + }) + case *hummingbird.SearchAssetsRequest_Owned: + assets, err = s.store.SearchOwnedAssets(ctx, &db.OwnedAssetQuery{ + AccountId: owner_id, + IA: (*addr.IA)(t.Owned.Ia), + Ingress: t.Owned.IfIdIngress, + Egress: t.Owned.IfIdEgress, + MinRequiredBandwidth: t.Owned.MinRequiredBw, + Price: t.Owned.Price, + StartsAt: t.Owned.StartsAtLatest, + StopsAt: t.Owned.StopsAtEarliest, + ValidityMode: db.ValidityMode(req.Msg.ValidityMode), + }) + default: + return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("invalid request")) } - - assets, err := s.store.Search(ctx, &db.AssetQuery{ - AccountId: owner_id, - IA: ia, - Ingress: req.Msg.IfIdIngress, - Egress: req.Msg.IfIdEgress, - MinRequiredBandwidth: req.Msg.MinRequiredBw, - Price: req.Msg.Price, - StartsAt: startsAt, - StopsAt: stopsAt, - Page: req.Msg.Page, - PageSize: pageSize, - }) if err != nil { return nil, connect.NewError(connect.CodeInvalidArgument, err) } diff --git a/marketplace/storage/storage.go b/marketplace/storage/storage.go index d198b32540..27e3c1edfb 100644 --- a/marketplace/storage/storage.go +++ b/marketplace/storage/storage.go @@ -70,11 +70,18 @@ func NewStorage( }, nil } -func (s *MarketplaceStorage) Search( +func (s *MarketplaceStorage) SearchListedAssets( ctx context.Context, - params *marketplacedb.AssetQuery, + params *marketplacedb.ListedAssetQuery, ) ([]*marketplacedb.DBAsset, error) { - return s.db.Search(ctx, params) + return s.db.SearchListedAssets(ctx, params) +} + +func (s *MarketplaceStorage) SearchOwnedAssets( + ctx context.Context, + params *marketplacedb.OwnedAssetQuery, +) ([]*marketplacedb.DBAsset, error) { + return s.db.SearchOwnedAssets(ctx, params) } func (s *MarketplaceStorage) FetchReservations( diff --git a/marketplace/webapp/web.go b/marketplace/webapp/web.go index fc35fb36f1..2d142eff02 100644 --- a/marketplace/webapp/web.go +++ b/marketplace/webapp/web.go @@ -512,10 +512,8 @@ func (h *Handler) assetsHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "forbidden", http.StatusForbidden) return } - assets, err := h.store.Search(r.Context(), &db.AssetQuery{ + assets, err := h.store.SearchOwnedAssets(r.Context(), &db.OwnedAssetQuery{ AccountId: &filterAccount.ID, - Page: 0, - PageSize: 1024, //TODO: add pagination also for GUI website }) if err != nil { log.Debug("User assets handler", "err", err) diff --git a/marketplace_client/main.go b/marketplace_client/main.go index 7d271ee3e7..1c7e507d4c 100644 --- a/marketplace_client/main.go +++ b/marketplace_client/main.go @@ -939,7 +939,7 @@ func handleSearch( reader *bufio.Reader, c hummingbirdconnect.MarketplaceServiceClient, ) { - var owned *bool + var owned bool var ia *uint64 var ingress *uint32 var egress *uint32 @@ -947,6 +947,8 @@ func handleSearch( var price *uint32 var startsAtLatest *time.Time var stopsAtEarliest *time.Time + var startsAtLatestUnix, stopsAtEarliestUnix *int64 + var validityPartial bool fmt.Println("Search Query. Owned is mandatory, other filters are ignored if empty.") owned = readOptionalBool(reader, "Owned (true,false): ") @@ -956,63 +958,98 @@ func handleSearch( minReqBw = readOptionalUint32(reader, "Min Required BW: ") price = readOptionalUint32(reader, "Price: ") startsAtLatest = readOptionalTime(reader, "Starts At Latest (2006-01-02T15:04:05Z): ") - stopsAtEarliest = readOptionalTime(reader, "Stops At Earliest (2006-01-02T15:04:05Z): ") - if owned == nil { - tmp := false - owned = &tmp - } - msg := &hummingbird.SearchAssetsRequest{ - Owned: *owned, - Ia: ia, - IfIdIngress: ingress, - IfIdEgress: egress, - MinRequiredBw: minReqBw, - Price: price, - } if startsAtLatest != nil { - msg.StartsAtLatest = timestamppb.New(*startsAtLatest) + tmp := startsAtLatest.Unix() + startsAtLatestUnix = &tmp } + stopsAtEarliest = readOptionalTime(reader, "Stops At Earliest (2006-01-02T15:04:05Z): ") if stopsAtEarliest != nil { - msg.StopsAtEarliest = timestamppb.New(*stopsAtEarliest) - } - for page := uint32(0); ; page++ { - msg.Page = page - rep, err := c.SearchAssets(ctx, &connect.Request[hummingbird.SearchAssetsRequest]{ - Msg: msg, - }) - if err != nil { - fmt.Println(err) - return + tmp := stopsAtEarliest.Unix() + stopsAtEarliestUnix = &tmp + } + validityPartial = readOptionalBool(reader, "Partial validity (true,false): ") + var msg *hummingbird.SearchAssetsRequest + validityMode := hummingbird.ValidityMode_full_overlap + if validityPartial { + validityMode = hummingbird.ValidityMode_partial_overlap + } + if owned { + msg = &hummingbird.SearchAssetsRequest{ + Request: &hummingbird.SearchAssetsRequest_Owned{ + Owned: &hummingbird.SearchOwnedAssetsRequest{ + Ia: ia, + IfIdIngress: ingress, + IfIdEgress: egress, + MinRequiredBw: minReqBw, + StartsAtLatest: startsAtLatestUnix, + StopsAtEarliest: stopsAtEarliestUnix, + Price: price, + }, + }, + ValidityMode: validityMode, } - transformed := make([]Asset, 0, len(rep.Msg.Assets)) - for _, asset := range rep.Msg.Assets { - transformed = append(transformed, Asset{ - ID: hex.EncodeToString(asset.AssetId), - IA: addr.IA(asset.Ia), - Bandwidth: asset.Bandwidth, - BandwidthMin: asset.BandwidthMin, - BandwidthMax: asset.BandwidthMax, - TimeMinDuration: asset.TimeMinDuration, - TimeMaxDuration: asset.TimeMaxDuration, - StartAt: asset.StartsAt.AsTime(), - StopsAt: asset.StopsAt.AsTime(), - Price: asset.Price, - IfIdIngress: asset.IfIdIngress, - IfIdEgress: asset.IfIdEgress, - TimeGranularity: asset.TimeGranularity, - }) + } else { + if ia == nil { + fmt.Println("ISD-AS required") + return } - j, err := json.MarshalIndent(transformed, "", "\t") - if err != nil { - fmt.Println(err) + if startsAtLatestUnix == nil { + fmt.Println("start at required") return } - fmt.Println(string(j)) - prompt := "More assets? [yes, no]: " - if !readConfirm(reader, &prompt) { + if stopsAtEarliestUnix == nil { + fmt.Println("stops at required") return } + if minReqBw == nil { + fmt.Println("bandwith required") + } + msg = &hummingbird.SearchAssetsRequest{ + Request: &hummingbird.SearchAssetsRequest_Listed{ + Listed: &hummingbird.SearchListedAssetsRequest{ + Ia: *ia, + IfIdIngress: ingress, + IfIdEgress: egress, + MinRequiredBw: *minReqBw, + StartsAtLatest: *startsAtLatestUnix, + StopsAtEarliest: *stopsAtEarliestUnix, + Price: price, + }, + }, + ValidityMode: validityMode, + } } + rep, err := c.SearchAssets(ctx, &connect.Request[hummingbird.SearchAssetsRequest]{ + Msg: msg, + }) + if err != nil { + fmt.Println(err) + return + } + transformed := make([]Asset, 0, len(rep.Msg.Assets)) + for _, asset := range rep.Msg.Assets { + transformed = append(transformed, Asset{ + ID: hex.EncodeToString(asset.AssetId), + IA: addr.IA(asset.Ia), + Bandwidth: asset.Bandwidth, + BandwidthMin: asset.BandwidthMin, + BandwidthMax: asset.BandwidthMax, + TimeMinDuration: asset.TimeMinDuration, + TimeMaxDuration: asset.TimeMaxDuration, + StartAt: asset.StartsAt.AsTime(), + StopsAt: asset.StopsAt.AsTime(), + Price: asset.Price, + IfIdIngress: asset.IfIdIngress, + IfIdEgress: asset.IfIdEgress, + TimeGranularity: asset.TimeGranularity, + }) + } + j, err := json.MarshalIndent(transformed, "", "\t") + if err != nil { + fmt.Println(err) + return + } + fmt.Println(string(j)) } func handleInfo(ctx context.Context, c hummingbirdconnect.MarketplaceServiceClient) { @@ -1140,23 +1177,23 @@ func readOptionalUint32(reader *bufio.Reader, prompt string) *uint32 { return &v } -func readOptionalBool(reader *bufio.Reader, prompt string) *bool { +func readOptionalBool(reader *bufio.Reader, prompt string) bool { fmt.Print(prompt) text, _ := reader.ReadString('\n') text = strings.TrimSpace(text) if text == "" { - return nil + return false } v, err := strconv.ParseBool(text) if err != nil { fmt.Println("Invalid bool") - return nil + return false } - return &v + return v } func readTime(reader *bufio.Reader, prompt string) time.Time { diff --git a/pkg/hummingbird/marketplace/client.go b/pkg/hummingbird/marketplace/client.go index 29f175e31b..5d15801ca4 100644 --- a/pkg/hummingbird/marketplace/client.go +++ b/pkg/hummingbird/marketplace/client.go @@ -352,34 +352,49 @@ func (c *Client) recursiveSelect( } func (c *Client) searchAllAssets(ctx context.Context, owned bool, ia *uint64, ingress *uint32, egress *uint32, - minBW uint32, startsAtLatest time.Time, stopsAtLatest time.Time) ([]*hummingbird.SearchAsset, error) { - // TODO: this is a temporary fix for pagination. We just fetch all pages - // To properly implement pagination, especially for the combine assets case, the recursive select algorithm would have to be changed. - var assets []*hummingbird.SearchAsset - for i := uint32(0); ; i++ { - resp, err := c.Marketplace.SearchAssets(ctx, &connect.Request[hummingbird.SearchAssetsRequest]{ + minBW uint32, startsAtLatest time.Time, stopsAtLatest time.Time, partialOverlap bool) ([]*hummingbird.SearchAsset, error) { + startsAtUnix := startsAtLatest.Unix() + stopsAtUnix := stopsAtLatest.Unix() + validityMode := hummingbird.ValidityMode_full_overlap + if partialOverlap { + validityMode = hummingbird.ValidityMode_partial_overlap + } + var resp *connect.Response[hummingbird.SearchAssetsResponse] + var err error + if owned { + resp, err = c.Marketplace.SearchAssets(ctx, &connect.Request[hummingbird.SearchAssetsRequest]{ Msg: &hummingbird.SearchAssetsRequest{ - Owned: owned, - Ia: ia, - IfIdIngress: ingress, - IfIdEgress: egress, - MinRequiredBw: &minBW, - StartsAtLatest: timestamppb.New(startsAtLatest), - StopsAtEarliest: timestamppb.New(stopsAtLatest), - Page: i, + Request: &hummingbird.SearchAssetsRequest_Owned{ + Owned: &hummingbird.SearchOwnedAssetsRequest{ + Ia: ia, + IfIdIngress: ingress, + IfIdEgress: egress, + MinRequiredBw: &minBW, + StartsAtLatest: &startsAtUnix, + StopsAtEarliest: &stopsAtUnix, + }, + }, + ValidityMode: validityMode, + }, + }) + } else { + resp, err = c.Marketplace.SearchAssets(ctx, &connect.Request[hummingbird.SearchAssetsRequest]{ + Msg: &hummingbird.SearchAssetsRequest{ + Request: &hummingbird.SearchAssetsRequest_Listed{ + Listed: &hummingbird.SearchListedAssetsRequest{ + Ia: *ia, + IfIdIngress: ingress, + IfIdEgress: egress, + MinRequiredBw: minBW, + StartsAtLatest: startsAtUnix, + StopsAtEarliest: stopsAtUnix, + }, + }, + ValidityMode: validityMode, }, }) - if err != nil { - return nil, err - } - if len(resp.Msg.Assets) == 0 { - break - } - for _, asset := range resp.Msg.Assets { - assets = append(assets, asset) - } } - return assets, nil + return resp.Msg.Assets, err } // checkoutAssetForInterfacePairWithCombine tries to find assets that can be combined on the time axis. It does not check for combinations on the bandwidth axis. @@ -390,7 +405,7 @@ func (c *Client) checkoutAssetForInterfacePairWithCombine(ctx context.Context, p var ingressBuyAssets, egressBuyAssets, pairBuyAssets []*hummingbird.BuyAsset var ingressAssetPrice, egressAssetPrice, pairAssetPrice uint64 var ok bool - ingressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, nil, bwInKbps, stopsAt, startsAt) + ingressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, nil, bwInKbps, startsAt, stopsAt, true) if err != nil { return nil, err } @@ -399,7 +414,7 @@ func (c *Client) checkoutAssetForInterfacePairWithCombine(ctx context.Context, p }) ingressBuyAssets, ingressAssetPrice, ok = c.recursiveSelectStart(ingressSearchAssets, bwInKbps, startsAt, stopsAt, combineCost) if ok { - egressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, nil, &pair.Egress, bwInKbps, stopsAt, startsAt) + egressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, nil, &pair.Egress, bwInKbps, startsAt, stopsAt, true) if err != nil { return nil, err } @@ -411,7 +426,7 @@ func (c *Client) checkoutAssetForInterfacePairWithCombine(ctx context.Context, p ingressAndEgressSuccess = true } } - pairAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, &pair.Egress, bwInKbps, stopsAt, startsAt) + pairAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, &pair.Egress, bwInKbps, startsAt, stopsAt, true) if err != nil { return nil, err } @@ -448,7 +463,7 @@ func (c *Client) checkoutAssetForInterfacePair( stopsAt time.Time, ) ([]*hummingbird.BuyAsset, error) { var ingressSearchAssets, egressSearchAssets, pairSearchAssets []*hummingbird.SearchAsset - ingressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, nil, bwInKbps, startsAt, stopsAt) + ingressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, nil, bwInKbps, startsAt, stopsAt, false) if err != nil { return nil, err } @@ -456,7 +471,7 @@ func (c *Client) checkoutAssetForInterfacePair( return a.IfIdEgress == nil }) if len(ingressSearchAssets) != 0 { - egressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, nil, &pair.Ingress, bwInKbps, startsAt, stopsAt) + egressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, nil, &pair.Ingress, bwInKbps, startsAt, stopsAt, false) if err != nil { return nil, err } @@ -464,7 +479,7 @@ func (c *Client) checkoutAssetForInterfacePair( return a.IfIdIngress == nil }) } - pairSearchAssets, err = c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, &pair.Egress, bwInKbps, startsAt, stopsAt) + pairSearchAssets, err = c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, &pair.Egress, bwInKbps, startsAt, stopsAt, false) if err != nil { return nil, err } @@ -630,17 +645,9 @@ func (c *Client) ObtainReservationsForInterfacePairs( // Look up the assets that were just bought, to learn where and when they apply. // If combineAssets == True, we need to find assets that "touch" the time window. // If not, we need to find assets which each one of them covers the time window. - var foundAssets []*hummingbird.SearchAsset - if combineAssets { - foundAssets, err = c.searchAllAssets(ctx, true, nil, nil, nil, bwInKbps, stopsAt, startsAt) - if err != nil { - return nil, err - } - } else { - foundAssets, err = c.searchAllAssets(ctx, true, nil, nil, nil, bwInKbps, startsAt, stopsAt) - if err != nil { - return nil, err - } + foundAssets, err := c.searchAllAssets(ctx, true, nil, nil, nil, bwInKbps, startsAt, stopsAt, combineAssets) + if err != nil { + return nil, err } foundAssetsMap := make(map[uint64][]*hummingbird.SearchAsset) diff --git a/pkg/proto/hummingbird/marketplace.pb.go b/pkg/proto/hummingbird/marketplace.pb.go index 06ea80ef2f..393dbe9de7 100644 --- a/pkg/proto/hummingbird/marketplace.pb.go +++ b/pkg/proto/hummingbird/marketplace.pb.go @@ -68,6 +68,52 @@ func (PricingStrategy) EnumDescriptor() ([]byte, []int) { return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{0} } +type ValidityMode int32 + +const ( + ValidityMode_full_overlap ValidityMode = 0 + ValidityMode_partial_overlap ValidityMode = 1 +) + +// Enum value maps for ValidityMode. +var ( + ValidityMode_name = map[int32]string{ + 0: "full_overlap", + 1: "partial_overlap", + } + ValidityMode_value = map[string]int32{ + "full_overlap": 0, + "partial_overlap": 1, + } +) + +func (x ValidityMode) Enum() *ValidityMode { + p := new(ValidityMode) + *p = x + return p +} + +func (x ValidityMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ValidityMode) Descriptor() protoreflect.EnumDescriptor { + return file_proto_hummingbird_v1_marketplace_proto_enumTypes[1].Descriptor() +} + +func (ValidityMode) Type() protoreflect.EnumType { + return &file_proto_hummingbird_v1_marketplace_proto_enumTypes[1] +} + +func (x ValidityMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ValidityMode.Descriptor instead. +func (ValidityMode) EnumDescriptor() ([]byte, []int) { + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{1} +} + type UpdateAssetsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Assets []*AssetUpdate `protobuf:"bytes,1,rep,name=assets,proto3" json:"assets,omitempty"` @@ -1227,19 +1273,15 @@ func (x *PublisherAsset) GetPrice() uint32 { } type SearchAssetsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Owned bool `protobuf:"varint,1,opt,name=owned,proto3" json:"owned,omitempty"` - Ia *uint64 `protobuf:"varint,2,opt,name=ia,proto3,oneof" json:"ia,omitempty"` - IfIdIngress *uint32 `protobuf:"varint,3,opt,name=if_id_ingress,json=ifIdIngress,proto3,oneof" json:"if_id_ingress,omitempty"` - IfIdEgress *uint32 `protobuf:"varint,4,opt,name=if_id_egress,json=ifIdEgress,proto3,oneof" json:"if_id_egress,omitempty"` - MinRequiredBw *uint32 `protobuf:"varint,5,opt,name=min_required_bw,json=minRequiredBw,proto3,oneof" json:"min_required_bw,omitempty"` - StartsAtLatest *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=starts_at_latest,json=startsAtLatest,proto3,oneof" json:"starts_at_latest,omitempty"` - StopsAtEarliest *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=stops_at_earliest,json=stopsAtEarliest,proto3,oneof" json:"stops_at_earliest,omitempty"` - Price *uint32 `protobuf:"varint,8,opt,name=price,proto3,oneof" json:"price,omitempty"` - Page uint32 `protobuf:"varint,9,opt,name=page,proto3" json:"page,omitempty"` - MaxReturnedAssets *uint32 `protobuf:"varint,10,opt,name=max_returned_assets,json=maxReturnedAssets,proto3,oneof" json:"max_returned_assets,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Request: + // + // *SearchAssetsRequest_Owned + // *SearchAssetsRequest_Listed + Request isSearchAssetsRequest_Request `protobuf_oneof:"request"` + ValidityMode ValidityMode `protobuf:"varint,3,opt,name=validity_mode,json=validityMode,proto3,enum=proto.hummingbird.v1.ValidityMode" json:"validity_mode,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SearchAssetsRequest) Reset() { @@ -1272,72 +1314,234 @@ func (*SearchAssetsRequest) Descriptor() ([]byte, []int) { return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{19} } -func (x *SearchAssetsRequest) GetOwned() bool { +func (x *SearchAssetsRequest) GetRequest() isSearchAssetsRequest_Request { if x != nil { - return x.Owned + return x.Request } - return false + return nil } -func (x *SearchAssetsRequest) GetIa() uint64 { +func (x *SearchAssetsRequest) GetOwned() *SearchOwnedAssetsRequest { + if x != nil { + if x, ok := x.Request.(*SearchAssetsRequest_Owned); ok { + return x.Owned + } + } + return nil +} + +func (x *SearchAssetsRequest) GetListed() *SearchListedAssetsRequest { + if x != nil { + if x, ok := x.Request.(*SearchAssetsRequest_Listed); ok { + return x.Listed + } + } + return nil +} + +func (x *SearchAssetsRequest) GetValidityMode() ValidityMode { + if x != nil { + return x.ValidityMode + } + return ValidityMode_full_overlap +} + +type isSearchAssetsRequest_Request interface { + isSearchAssetsRequest_Request() +} + +type SearchAssetsRequest_Owned struct { + Owned *SearchOwnedAssetsRequest `protobuf:"bytes,1,opt,name=owned,proto3,oneof"` +} + +type SearchAssetsRequest_Listed struct { + Listed *SearchListedAssetsRequest `protobuf:"bytes,2,opt,name=listed,proto3,oneof"` +} + +func (*SearchAssetsRequest_Owned) isSearchAssetsRequest_Request() {} + +func (*SearchAssetsRequest_Listed) isSearchAssetsRequest_Request() {} + +type SearchOwnedAssetsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ia *uint64 `protobuf:"varint,1,opt,name=ia,proto3,oneof" json:"ia,omitempty"` + IfIdIngress *uint32 `protobuf:"varint,2,opt,name=if_id_ingress,json=ifIdIngress,proto3,oneof" json:"if_id_ingress,omitempty"` + IfIdEgress *uint32 `protobuf:"varint,3,opt,name=if_id_egress,json=ifIdEgress,proto3,oneof" json:"if_id_egress,omitempty"` + MinRequiredBw *uint32 `protobuf:"varint,4,opt,name=min_required_bw,json=minRequiredBw,proto3,oneof" json:"min_required_bw,omitempty"` + StartsAtLatest *int64 `protobuf:"varint,5,opt,name=starts_at_latest,json=startsAtLatest,proto3,oneof" json:"starts_at_latest,omitempty"` + StopsAtEarliest *int64 `protobuf:"varint,6,opt,name=stops_at_earliest,json=stopsAtEarliest,proto3,oneof" json:"stops_at_earliest,omitempty"` + Price *uint32 `protobuf:"varint,7,opt,name=price,proto3,oneof" json:"price,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SearchOwnedAssetsRequest) Reset() { + *x = SearchOwnedAssetsRequest{} + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchOwnedAssetsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchOwnedAssetsRequest) ProtoMessage() {} + +func (x *SearchOwnedAssetsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchOwnedAssetsRequest.ProtoReflect.Descriptor instead. +func (*SearchOwnedAssetsRequest) Descriptor() ([]byte, []int) { + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{20} +} + +func (x *SearchOwnedAssetsRequest) GetIa() uint64 { if x != nil && x.Ia != nil { return *x.Ia } return 0 } -func (x *SearchAssetsRequest) GetIfIdIngress() uint32 { +func (x *SearchOwnedAssetsRequest) GetIfIdIngress() uint32 { if x != nil && x.IfIdIngress != nil { return *x.IfIdIngress } return 0 } -func (x *SearchAssetsRequest) GetIfIdEgress() uint32 { +func (x *SearchOwnedAssetsRequest) GetIfIdEgress() uint32 { if x != nil && x.IfIdEgress != nil { return *x.IfIdEgress } return 0 } -func (x *SearchAssetsRequest) GetMinRequiredBw() uint32 { +func (x *SearchOwnedAssetsRequest) GetMinRequiredBw() uint32 { if x != nil && x.MinRequiredBw != nil { return *x.MinRequiredBw } return 0 } -func (x *SearchAssetsRequest) GetStartsAtLatest() *timestamppb.Timestamp { - if x != nil { - return x.StartsAtLatest +func (x *SearchOwnedAssetsRequest) GetStartsAtLatest() int64 { + if x != nil && x.StartsAtLatest != nil { + return *x.StartsAtLatest } - return nil + return 0 } -func (x *SearchAssetsRequest) GetStopsAtEarliest() *timestamppb.Timestamp { - if x != nil { - return x.StopsAtEarliest +func (x *SearchOwnedAssetsRequest) GetStopsAtEarliest() int64 { + if x != nil && x.StopsAtEarliest != nil { + return *x.StopsAtEarliest } - return nil + return 0 } -func (x *SearchAssetsRequest) GetPrice() uint32 { +func (x *SearchOwnedAssetsRequest) GetPrice() uint32 { if x != nil && x.Price != nil { return *x.Price } return 0 } -func (x *SearchAssetsRequest) GetPage() uint32 { +type SearchListedAssetsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ia uint64 `protobuf:"varint,2,opt,name=ia,proto3" json:"ia,omitempty"` + IfIdIngress *uint32 `protobuf:"varint,3,opt,name=if_id_ingress,json=ifIdIngress,proto3,oneof" json:"if_id_ingress,omitempty"` + IfIdEgress *uint32 `protobuf:"varint,4,opt,name=if_id_egress,json=ifIdEgress,proto3,oneof" json:"if_id_egress,omitempty"` + MinRequiredBw uint32 `protobuf:"varint,5,opt,name=min_required_bw,json=minRequiredBw,proto3" json:"min_required_bw,omitempty"` + StartsAtLatest int64 `protobuf:"varint,6,opt,name=starts_at_latest,json=startsAtLatest,proto3" json:"starts_at_latest,omitempty"` + StopsAtEarliest int64 `protobuf:"varint,7,opt,name=stops_at_earliest,json=stopsAtEarliest,proto3" json:"stops_at_earliest,omitempty"` + Price *uint32 `protobuf:"varint,8,opt,name=price,proto3,oneof" json:"price,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SearchListedAssetsRequest) Reset() { + *x = SearchListedAssetsRequest{} + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchListedAssetsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchListedAssetsRequest) ProtoMessage() {} + +func (x *SearchListedAssetsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] if x != nil { - return x.Page + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchListedAssetsRequest.ProtoReflect.Descriptor instead. +func (*SearchListedAssetsRequest) Descriptor() ([]byte, []int) { + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{21} +} + +func (x *SearchListedAssetsRequest) GetIa() uint64 { + if x != nil { + return x.Ia } return 0 } -func (x *SearchAssetsRequest) GetMaxReturnedAssets() uint32 { - if x != nil && x.MaxReturnedAssets != nil { - return *x.MaxReturnedAssets +func (x *SearchListedAssetsRequest) GetIfIdIngress() uint32 { + if x != nil && x.IfIdIngress != nil { + return *x.IfIdIngress + } + return 0 +} + +func (x *SearchListedAssetsRequest) GetIfIdEgress() uint32 { + if x != nil && x.IfIdEgress != nil { + return *x.IfIdEgress + } + return 0 +} + +func (x *SearchListedAssetsRequest) GetMinRequiredBw() uint32 { + if x != nil { + return x.MinRequiredBw + } + return 0 +} + +func (x *SearchListedAssetsRequest) GetStartsAtLatest() int64 { + if x != nil { + return x.StartsAtLatest + } + return 0 +} + +func (x *SearchListedAssetsRequest) GetStopsAtEarliest() int64 { + if x != nil { + return x.StopsAtEarliest + } + return 0 +} + +func (x *SearchListedAssetsRequest) GetPrice() uint32 { + if x != nil && x.Price != nil { + return *x.Price } return 0 } @@ -1351,7 +1555,7 @@ type SearchAssetsResponse struct { func (x *SearchAssetsResponse) Reset() { *x = SearchAssetsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1363,7 +1567,7 @@ func (x *SearchAssetsResponse) String() string { func (*SearchAssetsResponse) ProtoMessage() {} func (x *SearchAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1376,7 +1580,7 @@ func (x *SearchAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchAssetsResponse.ProtoReflect.Descriptor instead. func (*SearchAssetsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{20} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{22} } func (x *SearchAssetsResponse) GetAssets() []*SearchAsset { @@ -1407,7 +1611,7 @@ type SearchAsset struct { func (x *SearchAsset) Reset() { *x = SearchAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1419,7 +1623,7 @@ func (x *SearchAsset) String() string { func (*SearchAsset) ProtoMessage() {} func (x *SearchAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1432,7 +1636,7 @@ func (x *SearchAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchAsset.ProtoReflect.Descriptor instead. func (*SearchAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{21} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{23} } func (x *SearchAsset) GetAssetId() []byte { @@ -1536,7 +1740,7 @@ type BuyAssetsRequest struct { func (x *BuyAssetsRequest) Reset() { *x = BuyAssetsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1548,7 +1752,7 @@ func (x *BuyAssetsRequest) String() string { func (*BuyAssetsRequest) ProtoMessage() {} func (x *BuyAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1561,7 +1765,7 @@ func (x *BuyAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAssetsRequest.ProtoReflect.Descriptor instead. func (*BuyAssetsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{22} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{24} } func (x *BuyAssetsRequest) GetAssets() []*BuyAsset { @@ -1590,7 +1794,7 @@ type BuyAsset struct { func (x *BuyAsset) Reset() { *x = BuyAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1602,7 +1806,7 @@ func (x *BuyAsset) String() string { func (*BuyAsset) ProtoMessage() {} func (x *BuyAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1615,7 +1819,7 @@ func (x *BuyAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAsset.ProtoReflect.Descriptor instead. func (*BuyAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{23} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{25} } func (x *BuyAsset) GetAssetId() []byte { @@ -1656,7 +1860,7 @@ type BuyAssetsResponse struct { func (x *BuyAssetsResponse) Reset() { *x = BuyAssetsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1668,7 +1872,7 @@ func (x *BuyAssetsResponse) String() string { func (*BuyAssetsResponse) ProtoMessage() {} func (x *BuyAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1681,7 +1885,7 @@ func (x *BuyAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAssetsResponse.ProtoReflect.Descriptor instead. func (*BuyAssetsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{24} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{26} } func (x *BuyAssetsResponse) GetAssets() []*BoughtAsset { @@ -1707,7 +1911,7 @@ type BoughtAsset struct { func (x *BoughtAsset) Reset() { *x = BoughtAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1719,7 +1923,7 @@ func (x *BoughtAsset) String() string { func (*BoughtAsset) ProtoMessage() {} func (x *BoughtAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1732,7 +1936,7 @@ func (x *BoughtAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use BoughtAsset.ProtoReflect.Descriptor instead. func (*BoughtAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{25} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{27} } func (x *BoughtAsset) GetAssetId() []byte { @@ -1755,7 +1959,7 @@ type RedeemAssetRequest struct { func (x *RedeemAssetRequest) Reset() { *x = RedeemAssetRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1767,7 +1971,7 @@ func (x *RedeemAssetRequest) String() string { func (*RedeemAssetRequest) ProtoMessage() {} func (x *RedeemAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1780,7 +1984,7 @@ func (x *RedeemAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemAssetRequest.ProtoReflect.Descriptor instead. func (*RedeemAssetRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{26} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{28} } func (x *RedeemAssetRequest) GetInterfaces() isRedeemAssetRequest_Interfaces { @@ -1817,7 +2021,7 @@ type RedeemAssetRequest_Pair struct { } type RedeemAssetRequest_IfPairAssetId struct { - IfPairAssetId []byte `protobuf:"bytes,3,opt,name=if_pair_asset_id,json=ifPairAssetId,proto3,oneof"` + IfPairAssetId []byte `protobuf:"bytes,2,opt,name=if_pair_asset_id,json=ifPairAssetId,proto3,oneof"` } func (*RedeemAssetRequest_Pair) isRedeemAssetRequest_Interfaces() {} @@ -1834,7 +2038,7 @@ type IngressEgressPair struct { func (x *IngressEgressPair) Reset() { *x = IngressEgressPair{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1846,7 +2050,7 @@ func (x *IngressEgressPair) String() string { func (*IngressEgressPair) ProtoMessage() {} func (x *IngressEgressPair) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1859,7 +2063,7 @@ func (x *IngressEgressPair) ProtoReflect() protoreflect.Message { // Deprecated: Use IngressEgressPair.ProtoReflect.Descriptor instead. func (*IngressEgressPair) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{27} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{29} } func (x *IngressEgressPair) GetIngressAssetId() []byte { @@ -1888,7 +2092,7 @@ type RedeemAssetResponse struct { func (x *RedeemAssetResponse) Reset() { *x = RedeemAssetResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1900,7 +2104,7 @@ func (x *RedeemAssetResponse) String() string { func (*RedeemAssetResponse) ProtoMessage() {} func (x *RedeemAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1913,7 +2117,7 @@ func (x *RedeemAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemAssetResponse.ProtoReflect.Descriptor instead. func (*RedeemAssetResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{28} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{30} } func (x *RedeemAssetResponse) GetAuthenticationKey() []byte { @@ -1958,7 +2162,7 @@ type FetchReservationsRequest struct { func (x *FetchReservationsRequest) Reset() { *x = FetchReservationsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1970,7 +2174,7 @@ func (x *FetchReservationsRequest) String() string { func (*FetchReservationsRequest) ProtoMessage() {} func (x *FetchReservationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1983,7 +2187,7 @@ func (x *FetchReservationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchReservationsRequest.ProtoReflect.Descriptor instead. func (*FetchReservationsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{29} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{31} } func (x *FetchReservationsRequest) GetIa() uint64 { @@ -2037,7 +2241,7 @@ type FetchReservationsResponse struct { func (x *FetchReservationsResponse) Reset() { *x = FetchReservationsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2049,7 +2253,7 @@ func (x *FetchReservationsResponse) String() string { func (*FetchReservationsResponse) ProtoMessage() {} func (x *FetchReservationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2062,7 +2266,7 @@ func (x *FetchReservationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchReservationsResponse.ProtoReflect.Descriptor instead. func (*FetchReservationsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{30} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{32} } func (x *FetchReservationsResponse) GetReservations() []*Reservation { @@ -2089,7 +2293,7 @@ type Reservation struct { func (x *Reservation) Reset() { *x = Reservation{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2101,7 +2305,7 @@ func (x *Reservation) String() string { func (*Reservation) ProtoMessage() {} func (x *Reservation) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2114,7 +2318,7 @@ func (x *Reservation) ProtoReflect() protoreflect.Message { // Deprecated: Use Reservation.ProtoReflect.Descriptor instead. func (*Reservation) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{31} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{33} } func (x *Reservation) GetReservationId() uint32 { @@ -2265,28 +2469,40 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + " \x01(\rR\x0ftimeMaxDuration\x12\x14\n" + "\x05price\x18\v \x01(\rR\x05priceB\x10\n" + "\x0e_if_id_ingressB\x0f\n" + - "\r_if_id_egress\"\xc4\x04\n" + - "\x13SearchAssetsRequest\x12\x14\n" + - "\x05owned\x18\x01 \x01(\bR\x05owned\x12\x13\n" + - "\x02ia\x18\x02 \x01(\x04H\x00R\x02ia\x88\x01\x01\x12'\n" + - "\rif_id_ingress\x18\x03 \x01(\rH\x01R\vifIdIngress\x88\x01\x01\x12%\n" + - "\fif_id_egress\x18\x04 \x01(\rH\x02R\n" + + "\r_if_id_egress\"\xfc\x01\n" + + "\x13SearchAssetsRequest\x12F\n" + + "\x05owned\x18\x01 \x01(\v2..proto.hummingbird.v1.SearchOwnedAssetsRequestH\x00R\x05owned\x12I\n" + + "\x06listed\x18\x02 \x01(\v2/.proto.hummingbird.v1.SearchListedAssetsRequestH\x00R\x06listed\x12G\n" + + "\rvalidity_mode\x18\x03 \x01(\x0e2\".proto.hummingbird.v1.ValidityModeR\fvalidityModeB\t\n" + + "\arequest\"\x9a\x03\n" + + "\x18SearchOwnedAssetsRequest\x12\x13\n" + + "\x02ia\x18\x01 \x01(\x04H\x00R\x02ia\x88\x01\x01\x12'\n" + + "\rif_id_ingress\x18\x02 \x01(\rH\x01R\vifIdIngress\x88\x01\x01\x12%\n" + + "\fif_id_egress\x18\x03 \x01(\rH\x02R\n" + "ifIdEgress\x88\x01\x01\x12+\n" + - "\x0fmin_required_bw\x18\x05 \x01(\rH\x03R\rminRequiredBw\x88\x01\x01\x12I\n" + - "\x10starts_at_latest\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampH\x04R\x0estartsAtLatest\x88\x01\x01\x12K\n" + - "\x11stops_at_earliest\x18\a \x01(\v2\x1a.google.protobuf.TimestampH\x05R\x0fstopsAtEarliest\x88\x01\x01\x12\x19\n" + - "\x05price\x18\b \x01(\rH\x06R\x05price\x88\x01\x01\x12\x12\n" + - "\x04page\x18\t \x01(\rR\x04page\x123\n" + - "\x13max_returned_assets\x18\n" + - " \x01(\rH\aR\x11maxReturnedAssets\x88\x01\x01B\x05\n" + + "\x0fmin_required_bw\x18\x04 \x01(\rH\x03R\rminRequiredBw\x88\x01\x01\x12-\n" + + "\x10starts_at_latest\x18\x05 \x01(\x03H\x04R\x0estartsAtLatest\x88\x01\x01\x12/\n" + + "\x11stops_at_earliest\x18\x06 \x01(\x03H\x05R\x0fstopsAtEarliest\x88\x01\x01\x12\x19\n" + + "\x05price\x18\a \x01(\rH\x06R\x05price\x88\x01\x01B\x05\n" + "\x03_iaB\x10\n" + "\x0e_if_id_ingressB\x0f\n" + "\r_if_id_egressB\x12\n" + "\x10_min_required_bwB\x13\n" + "\x11_starts_at_latestB\x14\n" + "\x12_stops_at_earliestB\b\n" + - "\x06_priceB\x16\n" + - "\x14_max_returned_assets\"Q\n" + + "\x06_price\"\xc1\x02\n" + + "\x19SearchListedAssetsRequest\x12\x0e\n" + + "\x02ia\x18\x02 \x01(\x04R\x02ia\x12'\n" + + "\rif_id_ingress\x18\x03 \x01(\rH\x00R\vifIdIngress\x88\x01\x01\x12%\n" + + "\fif_id_egress\x18\x04 \x01(\rH\x01R\n" + + "ifIdEgress\x88\x01\x01\x12&\n" + + "\x0fmin_required_bw\x18\x05 \x01(\rR\rminRequiredBw\x12(\n" + + "\x10starts_at_latest\x18\x06 \x01(\x03R\x0estartsAtLatest\x12*\n" + + "\x11stops_at_earliest\x18\a \x01(\x03R\x0fstopsAtEarliest\x12\x19\n" + + "\x05price\x18\b \x01(\rH\x02R\x05price\x88\x01\x01B\x10\n" + + "\x0e_if_id_ingressB\x0f\n" + + "\r_if_id_egressB\b\n" + + "\x06_price\"Q\n" + "\x14SearchAssetsResponse\x129\n" + "\x06assets\x18\x01 \x03(\v2!.proto.hummingbird.v1.SearchAssetR\x06assets\"\x9c\x04\n" + "\vSearchAsset\x12\x19\n" + @@ -2322,7 +2538,7 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\basset_id\x18\x01 \x01(\fR\aassetId\"\x8c\x01\n" + "\x12RedeemAssetRequest\x12=\n" + "\x04pair\x18\x01 \x01(\v2'.proto.hummingbird.v1.IngressEgressPairH\x00R\x04pair\x12)\n" + - "\x10if_pair_asset_id\x18\x03 \x01(\fH\x00R\rifPairAssetIdB\f\n" + + "\x10if_pair_asset_id\x18\x02 \x01(\fH\x00R\rifPairAssetIdB\f\n" + "\n" + "interfaces\"e\n" + "\x11IngressEgressPair\x12(\n" + @@ -2365,7 +2581,10 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\x12authentication_key\x18\t \x01(\fR\x11authenticationKey*:\n" + "\x0fPricingStrategy\x12\x12\n" + "\x0estatic_pricing\x10\x00\x12\x13\n" + - "\x0fdynamic_pricing\x10\x012\xa4\b\n" + + "\x0fdynamic_pricing\x10\x01*5\n" + + "\fValidityMode\x12\x10\n" + + "\ffull_overlap\x10\x00\x12\x13\n" + + "\x0fpartial_overlap\x10\x012\xa4\b\n" + "\x12MarketplaceService\x12e\n" + "\x04Info\x12,.proto.hummingbird.v1.MarketplaceInfoRequest\x1a-.proto.hummingbird.v1.MarketplaceInfoResponse\"\x00\x12g\n" + "\fPublishAsset\x12).proto.hummingbird.v1.PublishAssetRequest\x1a*.proto.hummingbird.v1.PublishAssetResponse\"\x00\x12g\n" + @@ -2392,99 +2611,103 @@ func file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP() []byte { return file_proto_hummingbird_v1_marketplace_proto_rawDescData } -var file_proto_hummingbird_v1_marketplace_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_hummingbird_v1_marketplace_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_proto_hummingbird_v1_marketplace_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_proto_hummingbird_v1_marketplace_proto_msgTypes = make([]protoimpl.MessageInfo, 34) var file_proto_hummingbird_v1_marketplace_proto_goTypes = []any{ (PricingStrategy)(0), // 0: proto.hummingbird.v1.PricingStrategy - (*UpdateAssetsRequest)(nil), // 1: proto.hummingbird.v1.UpdateAssetsRequest - (*AssetUpdate)(nil), // 2: proto.hummingbird.v1.AssetUpdate - (*Remove)(nil), // 3: proto.hummingbird.v1.Remove - (*UpdateAssetsResponse)(nil), // 4: proto.hummingbird.v1.UpdateAssetsResponse - (*UpdateAssetResult)(nil), // 5: proto.hummingbird.v1.UpdateAssetResult - (*StatisticsRequest)(nil), // 6: proto.hummingbird.v1.StatisticsRequest - (*StatisticsResponse)(nil), // 7: proto.hummingbird.v1.StatisticsResponse - (*StatisticsResponseEntry)(nil), // 8: proto.hummingbird.v1.StatisticsResponseEntry - (*SplitAssetRequest)(nil), // 9: proto.hummingbird.v1.SplitAssetRequest - (*SplitBwOption)(nil), // 10: proto.hummingbird.v1.SplitBwOption - (*SplitTimeOption)(nil), // 11: proto.hummingbird.v1.SplitTimeOption - (*SplitAssetResponse)(nil), // 12: proto.hummingbird.v1.SplitAssetResponse - (*CombineAssetRequest)(nil), // 13: proto.hummingbird.v1.CombineAssetRequest - (*CombineAssetResponse)(nil), // 14: proto.hummingbird.v1.CombineAssetResponse - (*MarketplaceInfoRequest)(nil), // 15: proto.hummingbird.v1.MarketplaceInfoRequest - (*MarketplaceInfoResponse)(nil), // 16: proto.hummingbird.v1.MarketplaceInfoResponse - (*PublishAssetRequest)(nil), // 17: proto.hummingbird.v1.PublishAssetRequest - (*PublishAssetResponse)(nil), // 18: proto.hummingbird.v1.PublishAssetResponse - (*PublisherAsset)(nil), // 19: proto.hummingbird.v1.PublisherAsset - (*SearchAssetsRequest)(nil), // 20: proto.hummingbird.v1.SearchAssetsRequest - (*SearchAssetsResponse)(nil), // 21: proto.hummingbird.v1.SearchAssetsResponse - (*SearchAsset)(nil), // 22: proto.hummingbird.v1.SearchAsset - (*BuyAssetsRequest)(nil), // 23: proto.hummingbird.v1.BuyAssetsRequest - (*BuyAsset)(nil), // 24: proto.hummingbird.v1.BuyAsset - (*BuyAssetsResponse)(nil), // 25: proto.hummingbird.v1.BuyAssetsResponse - (*BoughtAsset)(nil), // 26: proto.hummingbird.v1.BoughtAsset - (*RedeemAssetRequest)(nil), // 27: proto.hummingbird.v1.RedeemAssetRequest - (*IngressEgressPair)(nil), // 28: proto.hummingbird.v1.IngressEgressPair - (*RedeemAssetResponse)(nil), // 29: proto.hummingbird.v1.RedeemAssetResponse - (*FetchReservationsRequest)(nil), // 30: proto.hummingbird.v1.FetchReservationsRequest - (*FetchReservationsResponse)(nil), // 31: proto.hummingbird.v1.FetchReservationsResponse - (*Reservation)(nil), // 32: proto.hummingbird.v1.Reservation - (*timestamppb.Timestamp)(nil), // 33: google.protobuf.Timestamp + (ValidityMode)(0), // 1: proto.hummingbird.v1.ValidityMode + (*UpdateAssetsRequest)(nil), // 2: proto.hummingbird.v1.UpdateAssetsRequest + (*AssetUpdate)(nil), // 3: proto.hummingbird.v1.AssetUpdate + (*Remove)(nil), // 4: proto.hummingbird.v1.Remove + (*UpdateAssetsResponse)(nil), // 5: proto.hummingbird.v1.UpdateAssetsResponse + (*UpdateAssetResult)(nil), // 6: proto.hummingbird.v1.UpdateAssetResult + (*StatisticsRequest)(nil), // 7: proto.hummingbird.v1.StatisticsRequest + (*StatisticsResponse)(nil), // 8: proto.hummingbird.v1.StatisticsResponse + (*StatisticsResponseEntry)(nil), // 9: proto.hummingbird.v1.StatisticsResponseEntry + (*SplitAssetRequest)(nil), // 10: proto.hummingbird.v1.SplitAssetRequest + (*SplitBwOption)(nil), // 11: proto.hummingbird.v1.SplitBwOption + (*SplitTimeOption)(nil), // 12: proto.hummingbird.v1.SplitTimeOption + (*SplitAssetResponse)(nil), // 13: proto.hummingbird.v1.SplitAssetResponse + (*CombineAssetRequest)(nil), // 14: proto.hummingbird.v1.CombineAssetRequest + (*CombineAssetResponse)(nil), // 15: proto.hummingbird.v1.CombineAssetResponse + (*MarketplaceInfoRequest)(nil), // 16: proto.hummingbird.v1.MarketplaceInfoRequest + (*MarketplaceInfoResponse)(nil), // 17: proto.hummingbird.v1.MarketplaceInfoResponse + (*PublishAssetRequest)(nil), // 18: proto.hummingbird.v1.PublishAssetRequest + (*PublishAssetResponse)(nil), // 19: proto.hummingbird.v1.PublishAssetResponse + (*PublisherAsset)(nil), // 20: proto.hummingbird.v1.PublisherAsset + (*SearchAssetsRequest)(nil), // 21: proto.hummingbird.v1.SearchAssetsRequest + (*SearchOwnedAssetsRequest)(nil), // 22: proto.hummingbird.v1.SearchOwnedAssetsRequest + (*SearchListedAssetsRequest)(nil), // 23: proto.hummingbird.v1.SearchListedAssetsRequest + (*SearchAssetsResponse)(nil), // 24: proto.hummingbird.v1.SearchAssetsResponse + (*SearchAsset)(nil), // 25: proto.hummingbird.v1.SearchAsset + (*BuyAssetsRequest)(nil), // 26: proto.hummingbird.v1.BuyAssetsRequest + (*BuyAsset)(nil), // 27: proto.hummingbird.v1.BuyAsset + (*BuyAssetsResponse)(nil), // 28: proto.hummingbird.v1.BuyAssetsResponse + (*BoughtAsset)(nil), // 29: proto.hummingbird.v1.BoughtAsset + (*RedeemAssetRequest)(nil), // 30: proto.hummingbird.v1.RedeemAssetRequest + (*IngressEgressPair)(nil), // 31: proto.hummingbird.v1.IngressEgressPair + (*RedeemAssetResponse)(nil), // 32: proto.hummingbird.v1.RedeemAssetResponse + (*FetchReservationsRequest)(nil), // 33: proto.hummingbird.v1.FetchReservationsRequest + (*FetchReservationsResponse)(nil), // 34: proto.hummingbird.v1.FetchReservationsResponse + (*Reservation)(nil), // 35: proto.hummingbird.v1.Reservation + (*timestamppb.Timestamp)(nil), // 36: google.protobuf.Timestamp } var file_proto_hummingbird_v1_marketplace_proto_depIdxs = []int32{ - 2, // 0: proto.hummingbird.v1.UpdateAssetsRequest.assets:type_name -> proto.hummingbird.v1.AssetUpdate - 19, // 1: proto.hummingbird.v1.AssetUpdate.update:type_name -> proto.hummingbird.v1.PublisherAsset - 3, // 2: proto.hummingbird.v1.AssetUpdate.remove:type_name -> proto.hummingbird.v1.Remove - 5, // 3: proto.hummingbird.v1.UpdateAssetsResponse.result:type_name -> proto.hummingbird.v1.UpdateAssetResult - 33, // 4: proto.hummingbird.v1.StatisticsRequest.start:type_name -> google.protobuf.Timestamp - 33, // 5: proto.hummingbird.v1.StatisticsRequest.end:type_name -> google.protobuf.Timestamp - 8, // 6: proto.hummingbird.v1.StatisticsResponse.statistics:type_name -> proto.hummingbird.v1.StatisticsResponseEntry - 10, // 7: proto.hummingbird.v1.SplitAssetRequest.bw_split:type_name -> proto.hummingbird.v1.SplitBwOption - 11, // 8: proto.hummingbird.v1.SplitAssetRequest.time_split:type_name -> proto.hummingbird.v1.SplitTimeOption - 33, // 9: proto.hummingbird.v1.SplitTimeOption.splits:type_name -> google.protobuf.Timestamp + 3, // 0: proto.hummingbird.v1.UpdateAssetsRequest.assets:type_name -> proto.hummingbird.v1.AssetUpdate + 20, // 1: proto.hummingbird.v1.AssetUpdate.update:type_name -> proto.hummingbird.v1.PublisherAsset + 4, // 2: proto.hummingbird.v1.AssetUpdate.remove:type_name -> proto.hummingbird.v1.Remove + 6, // 3: proto.hummingbird.v1.UpdateAssetsResponse.result:type_name -> proto.hummingbird.v1.UpdateAssetResult + 36, // 4: proto.hummingbird.v1.StatisticsRequest.start:type_name -> google.protobuf.Timestamp + 36, // 5: proto.hummingbird.v1.StatisticsRequest.end:type_name -> google.protobuf.Timestamp + 9, // 6: proto.hummingbird.v1.StatisticsResponse.statistics:type_name -> proto.hummingbird.v1.StatisticsResponseEntry + 11, // 7: proto.hummingbird.v1.SplitAssetRequest.bw_split:type_name -> proto.hummingbird.v1.SplitBwOption + 12, // 8: proto.hummingbird.v1.SplitAssetRequest.time_split:type_name -> proto.hummingbird.v1.SplitTimeOption + 36, // 9: proto.hummingbird.v1.SplitTimeOption.splits:type_name -> google.protobuf.Timestamp 0, // 10: proto.hummingbird.v1.MarketplaceInfoResponse.pricing_strategy:type_name -> proto.hummingbird.v1.PricingStrategy - 19, // 11: proto.hummingbird.v1.PublishAssetRequest.asset:type_name -> proto.hummingbird.v1.PublisherAsset - 33, // 12: proto.hummingbird.v1.PublisherAsset.starts_at:type_name -> google.protobuf.Timestamp - 33, // 13: proto.hummingbird.v1.PublisherAsset.stops_at:type_name -> google.protobuf.Timestamp - 33, // 14: proto.hummingbird.v1.SearchAssetsRequest.starts_at_latest:type_name -> google.protobuf.Timestamp - 33, // 15: proto.hummingbird.v1.SearchAssetsRequest.stops_at_earliest:type_name -> google.protobuf.Timestamp - 22, // 16: proto.hummingbird.v1.SearchAssetsResponse.assets:type_name -> proto.hummingbird.v1.SearchAsset - 33, // 17: proto.hummingbird.v1.SearchAsset.starts_at:type_name -> google.protobuf.Timestamp - 33, // 18: proto.hummingbird.v1.SearchAsset.stops_at:type_name -> google.protobuf.Timestamp - 24, // 19: proto.hummingbird.v1.BuyAssetsRequest.assets:type_name -> proto.hummingbird.v1.BuyAsset - 33, // 20: proto.hummingbird.v1.BuyAsset.starts_at_exactly:type_name -> google.protobuf.Timestamp - 33, // 21: proto.hummingbird.v1.BuyAsset.stops_at_exactly:type_name -> google.protobuf.Timestamp - 26, // 22: proto.hummingbird.v1.BuyAssetsResponse.assets:type_name -> proto.hummingbird.v1.BoughtAsset - 28, // 23: proto.hummingbird.v1.RedeemAssetRequest.pair:type_name -> proto.hummingbird.v1.IngressEgressPair - 33, // 24: proto.hummingbird.v1.FetchReservationsRequest.starts_at:type_name -> google.protobuf.Timestamp - 33, // 25: proto.hummingbird.v1.FetchReservationsRequest.stops_at:type_name -> google.protobuf.Timestamp - 32, // 26: proto.hummingbird.v1.FetchReservationsResponse.reservations:type_name -> proto.hummingbird.v1.Reservation - 33, // 27: proto.hummingbird.v1.Reservation.starts_at:type_name -> google.protobuf.Timestamp - 33, // 28: proto.hummingbird.v1.Reservation.stops_at:type_name -> google.protobuf.Timestamp - 15, // 29: proto.hummingbird.v1.MarketplaceService.Info:input_type -> proto.hummingbird.v1.MarketplaceInfoRequest - 17, // 30: proto.hummingbird.v1.MarketplaceService.PublishAsset:input_type -> proto.hummingbird.v1.PublishAssetRequest - 20, // 31: proto.hummingbird.v1.MarketplaceService.SearchAssets:input_type -> proto.hummingbird.v1.SearchAssetsRequest - 23, // 32: proto.hummingbird.v1.MarketplaceService.BuyAssets:input_type -> proto.hummingbird.v1.BuyAssetsRequest - 27, // 33: proto.hummingbird.v1.MarketplaceService.RedeemAsset:input_type -> proto.hummingbird.v1.RedeemAssetRequest - 30, // 34: proto.hummingbird.v1.MarketplaceService.FetchReservations:input_type -> proto.hummingbird.v1.FetchReservationsRequest - 9, // 35: proto.hummingbird.v1.MarketplaceService.SplitAsset:input_type -> proto.hummingbird.v1.SplitAssetRequest - 13, // 36: proto.hummingbird.v1.MarketplaceService.CombineAssets:input_type -> proto.hummingbird.v1.CombineAssetRequest - 6, // 37: proto.hummingbird.v1.MarketplaceService.Statistics:input_type -> proto.hummingbird.v1.StatisticsRequest - 1, // 38: proto.hummingbird.v1.MarketplaceService.UpdateAssets:input_type -> proto.hummingbird.v1.UpdateAssetsRequest - 16, // 39: proto.hummingbird.v1.MarketplaceService.Info:output_type -> proto.hummingbird.v1.MarketplaceInfoResponse - 18, // 40: proto.hummingbird.v1.MarketplaceService.PublishAsset:output_type -> proto.hummingbird.v1.PublishAssetResponse - 21, // 41: proto.hummingbird.v1.MarketplaceService.SearchAssets:output_type -> proto.hummingbird.v1.SearchAssetsResponse - 25, // 42: proto.hummingbird.v1.MarketplaceService.BuyAssets:output_type -> proto.hummingbird.v1.BuyAssetsResponse - 29, // 43: proto.hummingbird.v1.MarketplaceService.RedeemAsset:output_type -> proto.hummingbird.v1.RedeemAssetResponse - 31, // 44: proto.hummingbird.v1.MarketplaceService.FetchReservations:output_type -> proto.hummingbird.v1.FetchReservationsResponse - 12, // 45: proto.hummingbird.v1.MarketplaceService.SplitAsset:output_type -> proto.hummingbird.v1.SplitAssetResponse - 14, // 46: proto.hummingbird.v1.MarketplaceService.CombineAssets:output_type -> proto.hummingbird.v1.CombineAssetResponse - 7, // 47: proto.hummingbird.v1.MarketplaceService.Statistics:output_type -> proto.hummingbird.v1.StatisticsResponse - 4, // 48: proto.hummingbird.v1.MarketplaceService.UpdateAssets:output_type -> proto.hummingbird.v1.UpdateAssetsResponse - 39, // [39:49] is the sub-list for method output_type - 29, // [29:39] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 20, // 11: proto.hummingbird.v1.PublishAssetRequest.asset:type_name -> proto.hummingbird.v1.PublisherAsset + 36, // 12: proto.hummingbird.v1.PublisherAsset.starts_at:type_name -> google.protobuf.Timestamp + 36, // 13: proto.hummingbird.v1.PublisherAsset.stops_at:type_name -> google.protobuf.Timestamp + 22, // 14: proto.hummingbird.v1.SearchAssetsRequest.owned:type_name -> proto.hummingbird.v1.SearchOwnedAssetsRequest + 23, // 15: proto.hummingbird.v1.SearchAssetsRequest.listed:type_name -> proto.hummingbird.v1.SearchListedAssetsRequest + 1, // 16: proto.hummingbird.v1.SearchAssetsRequest.validity_mode:type_name -> proto.hummingbird.v1.ValidityMode + 25, // 17: proto.hummingbird.v1.SearchAssetsResponse.assets:type_name -> proto.hummingbird.v1.SearchAsset + 36, // 18: proto.hummingbird.v1.SearchAsset.starts_at:type_name -> google.protobuf.Timestamp + 36, // 19: proto.hummingbird.v1.SearchAsset.stops_at:type_name -> google.protobuf.Timestamp + 27, // 20: proto.hummingbird.v1.BuyAssetsRequest.assets:type_name -> proto.hummingbird.v1.BuyAsset + 36, // 21: proto.hummingbird.v1.BuyAsset.starts_at_exactly:type_name -> google.protobuf.Timestamp + 36, // 22: proto.hummingbird.v1.BuyAsset.stops_at_exactly:type_name -> google.protobuf.Timestamp + 29, // 23: proto.hummingbird.v1.BuyAssetsResponse.assets:type_name -> proto.hummingbird.v1.BoughtAsset + 31, // 24: proto.hummingbird.v1.RedeemAssetRequest.pair:type_name -> proto.hummingbird.v1.IngressEgressPair + 36, // 25: proto.hummingbird.v1.FetchReservationsRequest.starts_at:type_name -> google.protobuf.Timestamp + 36, // 26: proto.hummingbird.v1.FetchReservationsRequest.stops_at:type_name -> google.protobuf.Timestamp + 35, // 27: proto.hummingbird.v1.FetchReservationsResponse.reservations:type_name -> proto.hummingbird.v1.Reservation + 36, // 28: proto.hummingbird.v1.Reservation.starts_at:type_name -> google.protobuf.Timestamp + 36, // 29: proto.hummingbird.v1.Reservation.stops_at:type_name -> google.protobuf.Timestamp + 16, // 30: proto.hummingbird.v1.MarketplaceService.Info:input_type -> proto.hummingbird.v1.MarketplaceInfoRequest + 18, // 31: proto.hummingbird.v1.MarketplaceService.PublishAsset:input_type -> proto.hummingbird.v1.PublishAssetRequest + 21, // 32: proto.hummingbird.v1.MarketplaceService.SearchAssets:input_type -> proto.hummingbird.v1.SearchAssetsRequest + 26, // 33: proto.hummingbird.v1.MarketplaceService.BuyAssets:input_type -> proto.hummingbird.v1.BuyAssetsRequest + 30, // 34: proto.hummingbird.v1.MarketplaceService.RedeemAsset:input_type -> proto.hummingbird.v1.RedeemAssetRequest + 33, // 35: proto.hummingbird.v1.MarketplaceService.FetchReservations:input_type -> proto.hummingbird.v1.FetchReservationsRequest + 10, // 36: proto.hummingbird.v1.MarketplaceService.SplitAsset:input_type -> proto.hummingbird.v1.SplitAssetRequest + 14, // 37: proto.hummingbird.v1.MarketplaceService.CombineAssets:input_type -> proto.hummingbird.v1.CombineAssetRequest + 7, // 38: proto.hummingbird.v1.MarketplaceService.Statistics:input_type -> proto.hummingbird.v1.StatisticsRequest + 2, // 39: proto.hummingbird.v1.MarketplaceService.UpdateAssets:input_type -> proto.hummingbird.v1.UpdateAssetsRequest + 17, // 40: proto.hummingbird.v1.MarketplaceService.Info:output_type -> proto.hummingbird.v1.MarketplaceInfoResponse + 19, // 41: proto.hummingbird.v1.MarketplaceService.PublishAsset:output_type -> proto.hummingbird.v1.PublishAssetResponse + 24, // 42: proto.hummingbird.v1.MarketplaceService.SearchAssets:output_type -> proto.hummingbird.v1.SearchAssetsResponse + 28, // 43: proto.hummingbird.v1.MarketplaceService.BuyAssets:output_type -> proto.hummingbird.v1.BuyAssetsResponse + 32, // 44: proto.hummingbird.v1.MarketplaceService.RedeemAsset:output_type -> proto.hummingbird.v1.RedeemAssetResponse + 34, // 45: proto.hummingbird.v1.MarketplaceService.FetchReservations:output_type -> proto.hummingbird.v1.FetchReservationsResponse + 13, // 46: proto.hummingbird.v1.MarketplaceService.SplitAsset:output_type -> proto.hummingbird.v1.SplitAssetResponse + 15, // 47: proto.hummingbird.v1.MarketplaceService.CombineAssets:output_type -> proto.hummingbird.v1.CombineAssetResponse + 8, // 48: proto.hummingbird.v1.MarketplaceService.Statistics:output_type -> proto.hummingbird.v1.StatisticsResponse + 5, // 49: proto.hummingbird.v1.MarketplaceService.UpdateAssets:output_type -> proto.hummingbird.v1.UpdateAssetsResponse + 40, // [40:50] is the sub-list for method output_type + 30, // [30:40] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_proto_hummingbird_v1_marketplace_proto_init() } @@ -2506,20 +2729,25 @@ func file_proto_hummingbird_v1_marketplace_proto_init() { (*SplitAssetRequest_TimeSplit)(nil), } file_proto_hummingbird_v1_marketplace_proto_msgTypes[18].OneofWrappers = []any{} - file_proto_hummingbird_v1_marketplace_proto_msgTypes[19].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[19].OneofWrappers = []any{ + (*SearchAssetsRequest_Owned)(nil), + (*SearchAssetsRequest_Listed)(nil), + } + file_proto_hummingbird_v1_marketplace_proto_msgTypes[20].OneofWrappers = []any{} file_proto_hummingbird_v1_marketplace_proto_msgTypes[21].OneofWrappers = []any{} - file_proto_hummingbird_v1_marketplace_proto_msgTypes[26].OneofWrappers = []any{ + file_proto_hummingbird_v1_marketplace_proto_msgTypes[23].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[28].OneofWrappers = []any{ (*RedeemAssetRequest_Pair)(nil), (*RedeemAssetRequest_IfPairAssetId)(nil), } - file_proto_hummingbird_v1_marketplace_proto_msgTypes[29].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[31].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_hummingbird_v1_marketplace_proto_rawDesc), len(file_proto_hummingbird_v1_marketplace_proto_rawDesc)), - NumEnums: 1, - NumMessages: 32, + NumEnums: 2, + NumMessages: 34, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/hummingbird/v1/marketplace.proto b/proto/hummingbird/v1/marketplace.proto index 46f0336bc6..1388f7c765 100644 --- a/proto/hummingbird/v1/marketplace.proto +++ b/proto/hummingbird/v1/marketplace.proto @@ -146,18 +146,37 @@ message PublisherAsset { uint32 price = 11; } +enum ValidityMode { + full_overlap = 0; + partial_overlap = 1; +} + message SearchAssetsRequest { - bool owned = 1; - optional uint64 ia = 2; + oneof request { + SearchOwnedAssetsRequest owned = 1; + SearchListedAssetsRequest listed = 2; + } + ValidityMode validity_mode = 3; +} + +message SearchOwnedAssetsRequest { + optional uint64 ia = 1; + optional uint32 if_id_ingress = 2; + optional uint32 if_id_egress = 3; + optional uint32 min_required_bw = 4; + optional int64 starts_at_latest = 5; + optional int64 stops_at_earliest = 6; + optional uint32 price = 7; +} + +message SearchListedAssetsRequest { + uint64 ia = 2; optional uint32 if_id_ingress = 3; optional uint32 if_id_egress = 4; - optional uint32 min_required_bw = 5; - optional google.protobuf.Timestamp starts_at_latest = 6; - optional google.protobuf.Timestamp stops_at_earliest = 7; + uint32 min_required_bw = 5; + int64 starts_at_latest = 6; + int64 stops_at_earliest = 7; optional uint32 price = 8; - uint32 page = 9; - // The marketplace may return less assets per page than requested. - optional uint32 max_returned_assets = 10; } message SearchAssetsResponse { @@ -203,7 +222,7 @@ message BoughtAsset { message RedeemAssetRequest { oneof interfaces { IngressEgressPair pair = 1; - bytes if_pair_asset_id = 3; + bytes if_pair_asset_id = 2; } } message IngressEgressPair { From db35fff15c89bf83f78cfa5e6c04ea906f94f29a Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:46:02 +0200 Subject: [PATCH 04/14] wip - support for search listed assets with partial overlap validity --- marketplace/db/sqlite.go | 150 +++++++++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 15 deletions(-) diff --git a/marketplace/db/sqlite.go b/marketplace/db/sqlite.go index 921cfdd171..3cf323170b 100644 --- a/marketplace/db/sqlite.go +++ b/marketplace/db/sqlite.go @@ -155,6 +155,50 @@ type rowScanner interface { Scan(dest ...any) error } +func scanAssetWithAvailableBandwidthAndUsability(row rowScanner) (*DBAsset, error) { + asset := &DBAsset{} + var startsAt int64 + var stopsAt int64 + var isd uint16 + var as uint64 + var usableFrom int64 + var usableTo int64 + var availableBW uint32 + if err := row.Scan( + &asset.ID, + &asset.AccountId, + &isd, + &as, + &asset.Bandwidth, + &asset.BandwidthMin, + &asset.BandwidthMax, + &asset.Price, + &asset.TimeGranularity, + &asset.TimeMinDuration, + &asset.TimeMaxDuration, + &startsAt, + &stopsAt, + &asset.IfIdIngress, + &asset.IfIdEgress, + &usableFrom, + &usableTo, + &availableBW, + ); err != nil { + return nil, serrors.Wrap("Error reading DB response", err) + } + var err error + asset.IA, err = addr.IAFrom(addr.ISD(isd), addr.AS(as)) + if err != nil { + return nil, err + } + asset.StartAt = time.Unix(startsAt, 0) + asset.StopsAt = time.Unix(stopsAt, 0) + asset.Bandwidth = availableBW + asset.StartAt = time.Unix(usableFrom, 0) + asset.StopsAt = time.Unix(usableTo, 0) + return asset, nil +} + func scanAssetWithAvailableBandwidth(row rowScanner) (*DBAsset, error) { asset := &DBAsset{} var startsAt int64 @@ -711,32 +755,108 @@ func (e *executor) SearchListedAssets(ctx context.Context, params *ListedAssetQu } var stmt string var args []any + var res []*DBAsset if params.ValidityMode == FullOverlap { stmt, args = e.buildSearchListedAssetsFullOverlapQuery(params) - fmt.Println(stmt, args) + //fmt.Println(stmt, args) + rows, err := e.read.QueryContext(ctx, stmt, args...) + if err != nil { + return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) + } + defer rows.Close() + + for rows.Next() { + a, err := scanAssetWithAvailableBandwidth(rows) + if err != nil { + return nil, err + } + res = append(res, a) + } } else if params.ValidityMode == PartialOverlap { stmt, args = e.buildSearchListedAssetsPartialOverlapQuery(params) - } else { - return nil, serrors.New("invalid validity mode") - } - rows, err := e.read.QueryContext(ctx, stmt, args...) - if err != nil { - return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) - } - defer rows.Close() - var res []*DBAsset - for rows.Next() { - a, err := scanAssetWithAvailableBandwidth(rows) + //fmt.Println(stmt, args) + rows, err := e.read.QueryContext(ctx, stmt, args...) if err != nil { - return nil, err + return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) } - res = append(res, a) + defer rows.Close() + for rows.Next() { + a, err := scanAssetWithAvailableBandwidthAndUsability(rows) + if err != nil { + return nil, err + } + res = append(res, a) + } + } else { + return nil, serrors.New("invalid validity mode") } + return res, nil } func (e *executor) buildSearchListedAssetsPartialOverlapQuery(params *ListedAssetQuery) (string, []any) { - return "", nil + var args []any + where := []string{} + queryStart := []string{` + WITH candidates AS ( + SELECT ` + assetColumnsWithAlias + ` + FROM Assets a + WHERE a.isd_id = ? AND a.as_id = ? + AND a.stops_at > ? AND a.starts_at < ? + AND a.account_id IS NULL + AND a.bandwidth >= ? + `} + queryEnd := ` + ), + hits AS ( + SELECT c.id AS asset_id, + MAX(g.starts_at, ?) AS f, + MIN(g.stops_at, ?) AS t, + g.available + FROM candidates c + JOIN Asset_Segment g + ON g.asset_id = c.id AND g.starts_at < ? AND g.stops_at > ? + WHERE g.available >= ? + ), + marked AS ( + SELECT *, + CASE WHEN f = LAG(t) OVER (PARTITION BY asset_id ORDER BY f) + THEN 0 ELSE 1 END AS new_island + FROM hits + ), + islands AS ( + SELECT *, + SUM(new_island) OVER (PARTITION BY asset_id ORDER BY f) AS island + FROM marked + ) + SELECT c.id, c.account_id, c.isd_id, c.as_id, c.bandwidth, c.bandwidth_min, c.bandwidth_max, + c.price, c.time_granularity, c.time_min_duration, c.time_max_duration, c.starts_at, c.stops_at, + c.ingress, c.egress, + MIN(i.f) AS usable_from, + MAX(i.t) AS usable_to, + MIN(i.available) AS max_buyable_bandwidth + FROM islands i + JOIN candidates c ON c.id = i.asset_id + GROUP BY i.asset_id, i.island + ORDER BY c.isd_id, c.as_id, c.ingress, c.egress, c.id, usable_from` + args = append(args, params.IA.ISD(), params.IA.AS(), params.StartsAt, params.StopsAt, params.MinRequiredBandwidth) + where = append(where, "(a.state = ?)") + args = append(args, AssetStateAvailable) + if params.Price != nil { + where = append(where, "(a.price<=?)") + args = append(args, *params.Price) + } + if params.Ingress != nil { + where = append(where, "(a.ingress=?)") + args = append(args, *params.Ingress) + } + if params.Egress != nil { + where = append(where, "(a.egress=?)") + args = append(args, *params.Egress) + } + args = append(args, params.StartsAt, params.StopsAt, params.StopsAt, params.StartsAt, params.MinRequiredBandwidth) + query := append(queryStart, fmt.Sprintf("AND %s", strings.Join(where, "AND\n")), queryEnd) + return strings.Join(query, "\n"), args } func (e *executor) buildSearchListedAssetsFullOverlapQuery(params *ListedAssetQuery) (string, []any) { From c178377a2670c475994fee89698e6b6dab7bb156 Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:05:07 +0200 Subject: [PATCH 05/14] tmp fix of unit tests --- marketplace/db/sqlite_test.go | 62 ++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/marketplace/db/sqlite_test.go b/marketplace/db/sqlite_test.go index c8e6b751d6..9616ec7f68 100644 --- a/marketplace/db/sqlite_test.go +++ b/marketplace/db/sqlite_test.go @@ -52,6 +52,18 @@ func testAsset() *DBAsset { } } +func insertAsset(ctx context.Context, repo Repository, asset *DBAsset) (int64, error) { + assetId, err := repo.InsertAsset(ctx, asset) + if err != nil { + return 0, err + } + if !asset.AccountId.Valid { + _, err = repo.InsertAssetSegment(ctx, assetId, + asset.StartAt.Unix(), asset.StopsAt.Unix(), asset.Bandwidth) + } + return assetId, err +} + // TestAssetIDInt64 rejects identifiers that cannot be represented by SQLite. func TestAssetIDInt64(t *testing.T) { id, err := AssetID(42).Int64() @@ -70,18 +82,23 @@ func TestWithTxCommitsAndRollsBack(t *testing.T) { // A successful callback commits its insert. err := backend.WithTx(ctx, func(repository Repository) error { - _, err := repository.InsertAsset(ctx, testAsset()) + _, err := insertAsset(ctx, repository, testAsset()) return err }) require.NoError(t, err) - assets, err := backend.Search(ctx, &AssetQuery{PageSize: 64}) + assets, err := backend.SearchListedAssets(ctx, &ListedAssetQuery{ + IA: addr.MustParseIA("1-ff00:0:110"), + StartsAt: 0, + StopsAt: math.MaxInt64, + ValidityMode: PartialOverlap, + }) require.NoError(t, err) require.Len(t, assets, 1) // Rolls back the insert and preserves the error. rollbackErr := errors.New("rollback") err = backend.WithTx(ctx, func(repository Repository) error { - _, err := repository.InsertAsset(ctx, testAsset()) + _, err := insertAsset(ctx, repository, testAsset()) if err != nil { return err } @@ -90,7 +107,12 @@ func TestWithTxCommitsAndRollsBack(t *testing.T) { require.ErrorIs(t, err, rollbackErr) // Only the asset written by the committed transaction is visible. - assets, err = backend.Search(ctx, &AssetQuery{PageSize: 64}) + assets, err = backend.SearchListedAssets(ctx, &ListedAssetQuery{ + IA: addr.MustParseIA("1-ff00:0:110"), + StartsAt: 0, + StopsAt: math.MaxInt64, + ValidityMode: PartialOverlap, + }) require.NoError(t, err) require.Len(t, assets, 1) } @@ -102,7 +124,7 @@ func TestAssetTransitionAndRowScanners(t *testing.T) { backend := newTestBackend(t) asset := testAsset() - assetID, err := backend.InsertAsset(ctx, asset) + assetID, err := insertAsset(ctx, backend, testAsset()) require.NoError(t, err) // Listing an asset without an owner lets the marketplace check it out. transitioned, err := backend.TransitionAsset(ctx, assetID, nil, @@ -113,7 +135,12 @@ func TestAssetTransitionAndRowScanners(t *testing.T) { require.False(t, transitioned.AccountId.Valid) // Checked-out assets are no longer returned by the available-asset search. - assets, err := backend.Search(ctx, &AssetQuery{PageSize: 64}) + assets, err := backend.SearchListedAssets(ctx, &ListedAssetQuery{ + IA: addr.MustParseIA("1-ff00:0:110"), + StartsAt: 0, + StopsAt: math.MaxInt64, + ValidityMode: PartialOverlap, + }) require.NoError(t, err) require.Empty(t, assets) @@ -193,11 +220,11 @@ func TestSearchOwnedAssets(t *testing.T) { owned := testAsset() owned.AccountId = sql.NullInt64{Int64: alicesAccountID, Valid: true} - ownedID, err := backend.InsertAsset(ctx, owned) + ownedID, err := insertAsset(ctx, backend, owned) require.NoError(t, err) // An asset nobody owns, and one owned by somebody else. - notOwnedAssetID, err := backend.InsertAsset(ctx, testAsset()) + notOwnedAssetID, err := insertAsset(ctx, backend, testAsset()) require.NoError(t, err) bobUserID, err := backend.CreateUser(ctx, &DBUser{Name: "bob", PasswordHash: "hash"}) require.NoError(t, err) @@ -205,10 +232,12 @@ func TestSearchOwnedAssets(t *testing.T) { require.NoError(t, err) otherAsset := testAsset() otherAsset.AccountId = sql.NullInt64{Int64: bobsAccountID, Valid: true} - _, err = backend.InsertAsset(ctx, otherAsset) + _, err = insertAsset(ctx, backend, otherAsset) require.NoError(t, err) - assets, err := backend.Search(ctx, &AssetQuery{AccountId: &alicesAccountID, PageSize: 64}) + assets, err := backend.SearchOwnedAssets(ctx, &OwnedAssetQuery{ + AccountId: &alicesAccountID, + }) require.NoError(t, err) require.Len(t, assets, 1) require.Equal(t, ownedID, assets[0].ID) @@ -216,27 +245,30 @@ func TestSearchOwnedAssets(t *testing.T) { // The validity filters still apply to the assets of the account. startsAt := owned.StartAt.Unix() stopsAt := owned.StopsAt.Unix() - assets, err = backend.Search(ctx, &AssetQuery{ + assets, err = backend.SearchOwnedAssets(ctx, &OwnedAssetQuery{ AccountId: &alicesAccountID, StartsAt: &startsAt, StopsAt: &stopsAt, - PageSize: 64, }) require.NoError(t, err) require.Len(t, assets, 1) tooLate := owned.StopsAt.Add(time.Hour).Unix() - assets, err = backend.Search(ctx, &AssetQuery{ + assets, err = backend.SearchOwnedAssets(ctx, &OwnedAssetQuery{ AccountId: &alicesAccountID, StartsAt: &startsAt, StopsAt: &tooLate, - PageSize: 64, }) require.NoError(t, err) require.Empty(t, assets, "an asset that stops too early must not be returned") // Without an account, only the assets nobody owns are listed. - assets, err = backend.Search(ctx, &AssetQuery{PageSize: 64}) + assets, err = backend.SearchListedAssets(ctx, &ListedAssetQuery{ + IA: addr.MustParseIA("1-ff00:0:110"), + StartsAt: 0, + StopsAt: math.MaxInt64, + ValidityMode: PartialOverlap, + }) require.NoError(t, err) require.Len(t, assets, 1) require.Equal(t, notOwnedAssetID, assets[0].ID) From ee3769631423542998fa8bc13336ad6820f213f0 Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Thu, 8 Oct 2026 15:54:12 +0200 Subject: [PATCH 06/14] search assets return interval, protobuf change timestamp types to int64 --- marketplace/BUILD.bazel | 1 - marketplace/db/schema.sql | 14 +- marketplace/db/sqlite.go | 147 ++----- marketplace/db/sqlite_test.go | 33 +- marketplace/db/types.go | 37 +- marketplace/export_test.go | 5 +- marketplace/id_store.go | 2 +- marketplace/id_store_test.go | 4 +- marketplace/redeem.go | 4 +- marketplace/redeem_test.go | 8 +- marketplace/redemption_service.go | 14 +- marketplace/service.go | 170 ++++---- marketplace/statistics_test.go | 6 +- marketplace/storage/BUILD.bazel | 1 - marketplace/storage/split.go | 27 +- marketplace/storage/split_test.go | 55 ++- marketplace/storage/storage.go | 171 +------- marketplace_client/BUILD.bazel | 1 - marketplace_client/main.go | 63 +-- pkg/hummingbird/marketplace/BUILD.bazel | 1 - pkg/hummingbird/marketplace/client.go | 286 ++++++++------ pkg/hummingbird/marketplace/client_test.go | 125 +++--- pkg/proto/hummingbird/marketplace.pb.go | 433 +++++++++++---------- pkg/proto/hummingbird/redemption.pb.go | 68 ++-- proto/hummingbird/v1/marketplace.proto | 45 +-- proto/hummingbird/v1/redemption.proto | 9 +- tools/topology/marketplace.py | 13 +- 27 files changed, 785 insertions(+), 958 deletions(-) diff --git a/marketplace/BUILD.bazel b/marketplace/BUILD.bazel index b43c2ef4bb..a39c3cc598 100644 --- a/marketplace/BUILD.bazel +++ b/marketplace/BUILD.bazel @@ -34,7 +34,6 @@ go_library( "//private/trust:go_default_library", "@com_connectrpc_connect//:go_default_library", "@com_github_golang_jwt_jwt//:go_default_library", - "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", "@org_golang_x_crypto//bcrypt:go_default_library", ], ) diff --git a/marketplace/db/schema.sql b/marketplace/db/schema.sql index 652f1ba370..1e4dc7d3a4 100644 --- a/marketplace/db/schema.sql +++ b/marketplace/db/schema.sql @@ -40,8 +40,8 @@ CREATE TABLE IF NOT EXISTS Reservations( egress INTEGER NOT NULL, bandwidth INTEGER NOT NULL, bw_encoded INTEGER NOT NULL, - starts_at TEXT NOT NULL, - stops_at TEXT NOT NULL, + starts_at INTEGER NOT NULL, + stops_at INTEGER NOT NULL, key BLOB NOT NULL, FOREIGN KEY (account_id) REFERENCES Accounts(id) ); @@ -79,8 +79,8 @@ CREATE TABLE IF NOT EXISTS Redemption_Delegations( as_id INTEGER NOT NULL, res_id_limit_low INTEGER NOT NULL DEFAULT 0, res_id_limit_high INTEGER NOT NULL, - expiration TEXT NOT NULL CHECK (expiration <= paid_until), - paid_until TEXT NOT NULL, + expiration INTEGER NOT NULL CHECK (expiration <= paid_until), + paid_until INTEGER NOT NULL, key BLOB NOT NULL, encodings BLOB NOT NULL, PRIMARY KEY(isd_id, as_id) @@ -92,10 +92,10 @@ CREATE TABLE IF NOT EXISTS Asset_Events( ingress INTEGER, egress INTEGER, bandwidth INTEGER NOT NULL, - starts_at TEXT NOT NULL, - stops_at TEXT NOT NULL, + starts_at INTEGER NOT NULL, + stops_at INTEGER NOT NULL, price INTEGER NOT NULL, event_type INTEGER NOT NULL, - event_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')) + event_at INTEGER NOT NULL DEFAULT (unixepoch()) ); CREATE INDEX IF NOT EXISTS idx_asset_events ON Asset_Events(event_type, isd_id, as_id, stops_at, starts_at); \ No newline at end of file diff --git a/marketplace/db/sqlite.go b/marketplace/db/sqlite.go index 3cf323170b..90878b1a2e 100644 --- a/marketplace/db/sqlite.go +++ b/marketplace/db/sqlite.go @@ -157,8 +157,6 @@ type rowScanner interface { func scanAssetWithAvailableBandwidthAndUsability(row rowScanner) (*DBAsset, error) { asset := &DBAsset{} - var startsAt int64 - var stopsAt int64 var isd uint16 var as uint64 var usableFrom int64 @@ -176,8 +174,8 @@ func scanAssetWithAvailableBandwidthAndUsability(row rowScanner) (*DBAsset, erro &asset.TimeGranularity, &asset.TimeMinDuration, &asset.TimeMaxDuration, - &startsAt, - &stopsAt, + &asset.StartAt, + &asset.StopsAt, &asset.IfIdIngress, &asset.IfIdEgress, &usableFrom, @@ -191,18 +189,14 @@ func scanAssetWithAvailableBandwidthAndUsability(row rowScanner) (*DBAsset, erro if err != nil { return nil, err } - asset.StartAt = time.Unix(startsAt, 0) - asset.StopsAt = time.Unix(stopsAt, 0) asset.Bandwidth = availableBW - asset.StartAt = time.Unix(usableFrom, 0) - asset.StopsAt = time.Unix(usableTo, 0) + asset.StartAt = usableFrom + asset.StopsAt = usableTo return asset, nil } func scanAssetWithAvailableBandwidth(row rowScanner) (*DBAsset, error) { asset := &DBAsset{} - var startsAt int64 - var stopsAt int64 var isd uint16 var as uint64 var availableBW uint32 @@ -218,8 +212,8 @@ func scanAssetWithAvailableBandwidth(row rowScanner) (*DBAsset, error) { &asset.TimeGranularity, &asset.TimeMinDuration, &asset.TimeMaxDuration, - &startsAt, - &stopsAt, + &asset.StartAt, + &asset.StopsAt, &asset.IfIdIngress, &asset.IfIdEgress, &availableBW, @@ -231,16 +225,12 @@ func scanAssetWithAvailableBandwidth(row rowScanner) (*DBAsset, error) { if err != nil { return nil, err } - asset.StartAt = time.Unix(startsAt, 0) - asset.StopsAt = time.Unix(stopsAt, 0) asset.Bandwidth = availableBW return asset, nil } func scanAsset(row rowScanner) (*DBAsset, error) { asset := &DBAsset{} - var startsAt int64 - var stopsAt int64 var isd uint16 var as uint64 if err := row.Scan( @@ -255,8 +245,8 @@ func scanAsset(row rowScanner) (*DBAsset, error) { &asset.TimeGranularity, &asset.TimeMinDuration, &asset.TimeMaxDuration, - &startsAt, - &stopsAt, + &asset.StartAt, + &asset.StopsAt, &asset.IfIdIngress, &asset.IfIdEgress, ); err != nil { @@ -267,15 +257,11 @@ func scanAsset(row rowScanner) (*DBAsset, error) { if err != nil { return nil, err } - asset.StartAt = time.Unix(startsAt, 0) - asset.StopsAt = time.Unix(stopsAt, 0) return asset, nil } func scanReservation(row rowScanner) (*DBReservation, error) { reservation := &DBReservation{} - var startsAt string - var stopsAt string var isd uint16 var as uint64 if err := row.Scan( @@ -288,8 +274,8 @@ func scanReservation(row rowScanner) (*DBReservation, error) { &reservation.Egress, &reservation.Bandwidth, &reservation.EncodedBandwidth, - &startsAt, - &stopsAt, + &reservation.StartsAt, + &reservation.StopsAt, &reservation.Key, ); err != nil { return nil, serrors.Wrap("Error reading DB response", err) @@ -299,14 +285,6 @@ func scanReservation(row rowScanner) (*DBReservation, error) { if err != nil { return nil, err } - reservation.StartsAt, err = time.Parse(time.RFC3339, startsAt) - if err != nil { - return nil, err - } - reservation.StopsAt, err = time.Parse(time.RFC3339, stopsAt) - if err != nil { - return nil, err - } return reservation, nil } @@ -382,7 +360,7 @@ func (e *executor) FindRedemptionDelegations( } stmt := `SELECT isd_id, as_id, res_id_limit_low, res_id_limit_high, expiration, paid_until, key, encodings FROM Redemption_Delegations WHERE expiration >= ?` - args := []any{time.Now().UTC().Format(time.RFC3339)} + args := []any{time.Now().Unix()} rows, err := e.read.QueryContext(ctx, stmt, args...) if err != nil { return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) @@ -391,8 +369,6 @@ func (e *executor) FindRedemptionDelegations( var res []*RedemptionDelegation for rows.Next() { a := &RedemptionDelegation{} - var expirationString string - var paidUntilString string var isd uint16 var as uint64 err = rows.Scan( @@ -400,21 +376,13 @@ func (e *executor) FindRedemptionDelegations( &as, &a.ResIdLow, &a.ResIdHigh, - &expirationString, - &paidUntilString, + &a.Expiration, + &a.PaidUntil, &a.Key, &a.Encodings) if err != nil { return nil, serrors.Wrap("Error reading DB response", err) } - a.Expiration, err = time.Parse(time.RFC3339, expirationString) - if err != nil { - return nil, err - } - a.PaidUntil, err = time.Parse(time.RFC3339, paidUntilString) - if err != nil { - return nil, err - } a.IA, err = addr.IAFrom(addr.ISD(isd), addr.AS(as)) res = append(res, a) } @@ -431,7 +399,7 @@ func (e *executor) FindRedemptionDelegation( stmt := `SELECT isd_id, as_id, res_id_limit_low, res_id_limit_high, expiration, paid_until, key, encodings FROM Redemption_Delegations WHERE expiration >= ? AND isd_id = ? AND as_id = ?` - rows, err := e.read.QueryContext(ctx, stmt, time.Now().Format(time.RFC3339), ia.ISD(), ia.AS()) + rows, err := e.read.QueryContext(ctx, stmt, time.Now().Unix(), ia.ISD(), ia.AS()) if err != nil { return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) } @@ -440,8 +408,6 @@ func (e *executor) FindRedemptionDelegation( return nil, nil } a := &RedemptionDelegation{} - var expirationString string - var paidUntilString string var isd uint16 var as uint64 err = rows.Scan( @@ -449,21 +415,13 @@ func (e *executor) FindRedemptionDelegation( &as, &a.ResIdLow, &a.ResIdHigh, - &expirationString, - &paidUntilString, + &a.Expiration, + &a.PaidUntil, &a.Key, &a.Encodings) if err != nil { return nil, serrors.Wrap("Error reading DB response", err) } - a.Expiration, err = time.Parse(time.RFC3339, expirationString) - if err != nil { - return nil, err - } - a.PaidUntil, err = time.Parse(time.RFC3339, paidUntilString) - if err != nil { - return nil, err - } a.IA, err = addr.IAFrom(addr.ISD(isd), addr.AS(as)) return a, nil } @@ -569,8 +527,8 @@ func (e *executor) CreateOrUpdateRedemptionDelegations( r.IA.AS(), r.ResIdLow, r.ResIdHigh, - r.Expiration.UTC().Format(time.RFC3339), - r.PaidUntil.UTC().Format(time.RFC3339), + r.Expiration, + r.PaidUntil, r.Key, r.Encodings) if err != nil { @@ -595,20 +553,10 @@ func (e *executor) FindUsedReservations( var res []*UsedReservation for rows.Next() { a := &UsedReservation{} - var startsAtString string - var stopsAtString string - err = rows.Scan(&a.Id, &startsAtString, &stopsAtString) + err = rows.Scan(&a.Id, &a.StartsAt, &a.StopsAt) if err != nil { return nil, serrors.Wrap("Error reading DB response", err) } - a.StartsAt, err = time.Parse(time.RFC3339, startsAtString) - if err != nil { - return nil, err - } - a.StopsAt, err = time.Parse(time.RFC3339, stopsAtString) - if err != nil { - return nil, err - } res = append(res, a) } return res, nil @@ -623,7 +571,7 @@ func (e *executor) buildUsedReservationsQuery(params *UsedReservationsQuery) (st params.IA.AS(), params.Limit_low, params.Limit_high, - time.Now().UTC().Format(time.RFC3339), + time.Now().Unix(), } return query, args } @@ -641,20 +589,10 @@ func (e *executor) SearchAssetsForStatistics(ctx context.Context, params *Statis var publishedAssets []*DBStat for publishRows.Next() { a := &DBStat{} - var startsAtString string - var stopsAtString string - err = publishRows.Scan(&a.Bandwidth, &a.Price, &startsAtString, &stopsAtString) + err = publishRows.Scan(&a.Bandwidth, &a.Price, &a.StartsAt, &a.StopsAt) if err != nil { return nil, nil, serrors.Wrap("Error reading DB response", err) } - a.StartsAt, err = time.Parse(time.RFC3339, startsAtString) - if err != nil { - return nil, nil, err - } - a.StopsAt, err = time.Parse(time.RFC3339, stopsAtString) - if err != nil { - return nil, nil, err - } publishedAssets = append(publishedAssets, a) } boughtStmt, args := e.buildAssetEventsQuery(params, AssetBought) @@ -666,20 +604,10 @@ func (e *executor) SearchAssetsForStatistics(ctx context.Context, params *Statis var boughtAssets []*DBStat for boughtRows.Next() { a := &DBStat{} - var startsAtString string - var stopsAtString string - err = boughtRows.Scan(&a.Bandwidth, &a.Price, &startsAtString, &stopsAtString) + err = boughtRows.Scan(&a.Bandwidth, &a.Price, &a.StartsAt, &a.StopsAt) if err != nil { return nil, nil, serrors.Wrap("Error reading DB response", err) } - a.StartsAt, err = time.Parse(time.RFC3339, startsAtString) - if err != nil { - return nil, nil, err - } - a.StopsAt, err = time.Parse(time.RFC3339, stopsAtString) - if err != nil { - return nil, nil, err - } boughtAssets = append(boughtAssets, a) } return publishedAssets, boughtAssets, nil @@ -713,7 +641,7 @@ func (e *executor) RegisterAssetEvent(ctx context.Context, a *DBAsset, eventType inst := `INSERT INTO Asset_Events (event_type, isd_id, as_id, ingress, egress, bandwidth, starts_at, stops_at, price) VALUES(?,?,?,?,?,?,?,?,?)` res, err := e.write.ExecContext(ctx, inst, eventType, a.IA.ISD(), a.IA.AS(), a.IfIdIngress, a.IfIdEgress, a.Bandwidth, - a.StartAt.UTC().Format(time.RFC3339), a.StopsAt.UTC().Format(time.RFC3339), a.Price) + a.StartAt, a.StopsAt, a.Price) if err != nil { return 0, err } @@ -758,7 +686,6 @@ func (e *executor) SearchListedAssets(ctx context.Context, params *ListedAssetQu var res []*DBAsset if params.ValidityMode == FullOverlap { stmt, args = e.buildSearchListedAssetsFullOverlapQuery(params) - //fmt.Println(stmt, args) rows, err := e.read.QueryContext(ctx, stmt, args...) if err != nil { return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) @@ -774,7 +701,6 @@ func (e *executor) SearchListedAssets(ctx context.Context, params *ListedAssetQu } } else if params.ValidityMode == PartialOverlap { stmt, args = e.buildSearchListedAssetsPartialOverlapQuery(params) - //fmt.Println(stmt, args) rows, err := e.read.QueryContext(ctx, stmt, args...) if err != nil { return nil, serrors.New("Error looking up assets", "err", err, "q", stmt) @@ -1041,8 +967,8 @@ func (e *executor) InsertReservation(ctx context.Context, r *DBReservation) (int r.Egress, r.Bandwidth, r.EncodedBandwidth, - r.StartsAt.UTC().Format(time.RFC3339), - r.StopsAt.UTC().Format(time.RFC3339), + r.StartsAt, + r.StopsAt, r.Key, r.AccountId) if err != nil { @@ -1072,8 +998,8 @@ func (e *executor) InsertAsset(ctx context.Context, a *DBAsset) (int64, error) { a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, - a.StartAt.Unix(), - a.StopsAt.Unix(), + a.StartAt, + a.StopsAt, a.IfIdIngress, a.IfIdEgress, a.AccountId) @@ -1090,8 +1016,8 @@ func (e *executor) InsertAsset(ctx context.Context, a *DBAsset) (int64, error) { a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, - a.StartAt.Unix(), - a.StopsAt.Unix(), + a.StartAt, + a.StopsAt, a.IfIdIngress, a.IfIdEgress) } @@ -1501,8 +1427,6 @@ func (e *executor) BuyAsset( AND g.starts_at < ? AND g.stops_at > ? AND g.available < ?); ` - fmt.Println(inst) - fmt.Println(assetId, AssetStateAvailable, startsAt, stopsAt, stopsAt, startsAt, bw) rows, err := e.write.QueryContext(ctx, inst, assetId, AssetStateAvailable, startsAt, stopsAt, stopsAt, startsAt, bw) if err != nil { return nil, err @@ -1538,8 +1462,8 @@ func (e *executor) BuyAsset( }, IA: asset.IA, Bandwidth: bw, - StartAt: time.Unix(startsAt, 0), - StopsAt: time.Unix(stopsAt, 0), + StartAt: startsAt, + StopsAt: stopsAt, BandwidthMin: asset.BandwidthMin, BandwidthMax: asset.BandwidthMax, Price: asset.Price, @@ -1554,15 +1478,6 @@ func (e *executor) BuyAsset( return nil, err } userAsset.ID = newId - // TODO: we still need a new asset ID for the now user-owned asset. Maybe include it here? - /*recordPurchaseInst := ` - INSERT INTO purchase (order_id, asset_id, bandwidth, starts_at, stops_at) - VALUES (?, ?, ?, ?, ?) - ` - _, err = e.write.ExecContext(ctx, recordPurchaseInst, orderId, assetId, bw, startsAt, stopsAt) - if err != nil { - return 0, err - }*/ err = mergeAt(startsAt) if err != nil { return nil, err diff --git a/marketplace/db/sqlite_test.go b/marketplace/db/sqlite_test.go index 9616ec7f68..23f25c5781 100644 --- a/marketplace/db/sqlite_test.go +++ b/marketplace/db/sqlite_test.go @@ -47,8 +47,8 @@ func testAsset() *DBAsset { Price: 5, TimeGranularity: 60, TimeMinDuration: 60, - StartAt: start, - StopsAt: start.Add(time.Hour), + StartAt: start.Unix(), + StopsAt: start.Add(time.Hour).Unix(), } } @@ -59,7 +59,7 @@ func insertAsset(ctx context.Context, repo Repository, asset *DBAsset) (int64, e } if !asset.AccountId.Valid { _, err = repo.InsertAssetSegment(ctx, assetId, - asset.StartAt.Unix(), asset.StopsAt.Unix(), asset.Bandwidth) + asset.StartAt, asset.StopsAt, asset.Bandwidth) } return assetId, err } @@ -243,8 +243,8 @@ func TestSearchOwnedAssets(t *testing.T) { require.Equal(t, ownedID, assets[0].ID) // The validity filters still apply to the assets of the account. - startsAt := owned.StartAt.Unix() - stopsAt := owned.StopsAt.Unix() + startsAt := owned.StartAt + stopsAt := owned.StopsAt assets, err = backend.SearchOwnedAssets(ctx, &OwnedAssetQuery{ AccountId: &alicesAccountID, StartsAt: &startsAt, @@ -253,7 +253,7 @@ func TestSearchOwnedAssets(t *testing.T) { require.NoError(t, err) require.Len(t, assets, 1) - tooLate := owned.StopsAt.Add(time.Hour).Unix() + tooLate := owned.StopsAt + 60*60 assets, err = backend.SearchOwnedAssets(ctx, &OwnedAssetQuery{ AccountId: &alicesAccountID, StartsAt: &startsAt, @@ -279,11 +279,12 @@ func TestCreateOrUpdateRedemptionDelegations(t *testing.T) { ctx := context.Background() backend := newTestBackend(t) ia := addr.MustParseIA("1-ff00:0:110") + currentYear := time.Now().Year() // first try creating a redemption delegation delegationToInsert := &RedemptionDelegation{ IA: ia, - Expiration: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC), - PaidUntil: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC), + Expiration: time.Date(currentYear+1, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), + PaidUntil: time.Date(currentYear+1, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), ResIdLow: 0, ResIdHigh: 1024, Key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, @@ -303,8 +304,8 @@ func TestCreateOrUpdateRedemptionDelegations(t *testing.T) { // now try to update a redemption delegation delegationToUpdate := &RedemptionDelegation{ IA: ia, - Expiration: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), - PaidUntil: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), + Expiration: time.Date(currentYear+2, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), + PaidUntil: time.Date(currentYear+2, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), ResIdLow: 1024, ResIdHigh: 2048, Key: []byte{16, 15, 14, 16, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, @@ -328,8 +329,8 @@ func TestRegisterAssetEvent(t *testing.T) { Bandwidth: 1000, BandwidthMin: 10, BandwidthMax: 1000, - StartAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC), - StopsAt: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), + StartAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), + StopsAt: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), TimeGranularity: 10, TimeMinDuration: 10, TimeMaxDuration: 3600, @@ -347,8 +348,8 @@ func TestRegisterAssetEvent(t *testing.T) { Bandwidth: 1000, BandwidthMin: 10, BandwidthMax: 1000, - StartAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC), - StopsAt: time.Date(2027, 1, 2, 0, 0, 0, 0, time.UTC), + StartAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), + StopsAt: time.Date(2027, 1, 2, 0, 0, 0, 0, time.UTC).Unix(), TimeGranularity: 10, TimeMinDuration: 10, TimeMaxDuration: 3600, @@ -362,8 +363,8 @@ func TestRegisterAssetEvent(t *testing.T) { // now verify whether both registered events return the correct values. publishStats, buyStats, err := backend.SearchAssetsForStatistics(ctx, &StatisticsQuery{ IA: ia, - WindowStart: "2027-01-01T00:00:00Z", - WindowEnd: "2028-01-01T00:00:00Z", + WindowStart: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), + WindowEnd: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), }) require.NoError(t, err) require.Len(t, publishStats, 1) diff --git a/marketplace/db/types.go b/marketplace/db/types.go index f51574401b..6600dc5a84 100644 --- a/marketplace/db/types.go +++ b/marketplace/db/types.go @@ -18,7 +18,6 @@ import ( "database/sql" "encoding/binary" "math" - "time" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/private/serrors" @@ -113,18 +112,18 @@ type UsedReservation struct { // The reservation ID Id uint32 // inclusive start time of the reservation - StartsAt time.Time + StartsAt int64 // exclusive end time of the reservation - StopsAt time.Time + StopsAt int64 } type RedemptionDelegation struct { // ISD-AS IA addr.IA // expiration time of redemption delegation - Expiration time.Time + Expiration int64 // until when redemption delegation is paid - PaidUntil time.Time + PaidUntil int64 // the inclusive lower bound reservation ID used for redemption ResIdLow uint32 // the exclusive upper bound reservation ID used for redemption @@ -144,20 +143,20 @@ type ReservationQuery struct { Ingress *uint32 // ingress ID of the reservation Egress *uint32 - // reservation starts at latest in RFC3339 format - StartsAt *string - // reservation stops at earliest in RFC3339 format - StopsAt *string + // reservation starts at latest in unix seconds + StartsAt *int64 + // reservation stops at earliest in unix seconds + StopsAt *int64 // minimum bandwith the reservation holds Bandwidth *uint32 } type StatisticsQuery struct { // ISD-AS for which statistics should be queried IA addr.IA - // time window start in RFC3339 format - WindowStart string - // time window end in RFC3339 format - WindowEnd string + // time window start in unix seconds + WindowStart int64 + // time window end in unix seconds + WindowEnd int64 // ingress ID of the assets Ingress *uint32 // egress ID of the assets @@ -169,9 +168,9 @@ type DBStat struct { // the bandwidth of the asset Bandwidth int64 // the inclusive start time of the asset - StartsAt time.Time + StartsAt int64 // the exclusive end time of the asset - StopsAt time.Time + StopsAt int64 } type DBReservation struct { @@ -190,9 +189,9 @@ type DBReservation struct { // the dataplane encoded bandwidth of the reservation EncodedBandwidth uint16 // inclusive start time of the reservation - StartsAt time.Time + StartsAt int64 // exclusive end time of the reservation - StopsAt time.Time + StopsAt int64 // the account ID of the owner AccountId int64 // the cryptographic key @@ -213,9 +212,9 @@ type DBAsset struct { // the maximum bandwidth allowed at redemption BandwidthMax uint32 // inclusive start time of the asset - StartAt time.Time + StartAt int64 // exclusive end time of the asset - StopsAt time.Time + StopsAt int64 // price per kbps per second of the asset Price uint32 // asset duration must be divisible by time granularity at redemption diff --git a/marketplace/export_test.go b/marketplace/export_test.go index 305ec7ad78..c1cb5d0dd7 100644 --- a/marketplace/export_test.go +++ b/marketplace/export_test.go @@ -59,7 +59,8 @@ func StatisticsWindow( start, end time.Time, step, granularity time.Duration, ) (time.Time, time.Time, int) { - return statisticsWindow(start, end, step, granularity) + window_start, window_end, num_intervals := statisticsWindow(start.Unix(), end.Unix(), int64(step/time.Second), int64(granularity/time.Second)) + return time.Unix(window_start, 0).UTC(), time.Unix(window_end, 0).UTC(), int(num_intervals) } // BandwidthUtilization exposes the share of the published bandwidth that was bought. @@ -74,5 +75,5 @@ func BandwidthPerInterval( step time.Duration, numIntervals int, ) (bandwidth, income []uint64) { - return bandwidthPerInterval(assets, windowStart, windowEnd, step, numIntervals) + return bandwidthPerInterval(assets, windowStart.Unix(), windowEnd.Unix(), int64(step/time.Second), int64(numIntervals)) } diff --git a/marketplace/id_store.go b/marketplace/id_store.go index 3351514d2c..e2b6c22dcc 100644 --- a/marketplace/id_store.go +++ b/marketplace/id_store.go @@ -71,7 +71,7 @@ func (s *UsedIDStore) Init(limit_low uint32, limit_high uint32, r []*db.UsedRese s.usedIds[res.Id] = struct{}{} s.expirations = append(s.expirations, &entry{ id: res.Id, - expiration: res.StopsAt.Unix(), + expiration: res.StopsAt, }) } heap.Init(&s.expirations) diff --git a/marketplace/id_store_test.go b/marketplace/id_store_test.go index da0b717ba4..337a4b40a0 100644 --- a/marketplace/id_store_test.go +++ b/marketplace/id_store_test.go @@ -31,8 +31,8 @@ func TestIDStoreNext(t *testing.T) { err = store.Init(0, 3, []*db.UsedReservation{ { Id: 0, - StartsAt: base, - StopsAt: base.Add(time.Second * 3), + StartsAt: b, + StopsAt: b + 3, }, }) assert.NoError(t, err) diff --git a/marketplace/redeem.go b/marketplace/redeem.go index 02fbe819dd..b272f62fd4 100644 --- a/marketplace/redeem.go +++ b/marketplace/redeem.go @@ -113,7 +113,7 @@ func (h *RedemptionServerHandler) Redeem( req *hummingbird.RedeemAssetFromASRequest, ) (*hummingbird.RedeemAssetFromASResponse, error) { h.mu.Lock() - if h.local != nil && h.local.expiration.After(time.Now()) { + if h.local != nil && h.local.expiration > time.Now().Unix() { // The marketplace holds a delegation, so it redeems the asset itself. // Redeem only touches memory, so the lock is held across it. resp := h.local.Redeem(req) @@ -364,7 +364,7 @@ func (s *Service) DelegateRedemption( } handler := s.FindRedemptionServerHandler(clientID) err := handler.ApplyDelegation(ctx, &RedemptionDelegationUpdate{ - ExpirationTime: req.Msg.ExpirationTime.AsTime(), + ExpirationTime: req.Msg.ExpirationTime, IdLimitLow: req.Msg.ReservationIdLowerBound, IdLimitHigh: req.Msg.ReservationIdUpperBound, Key: req.Msg.Key, diff --git a/marketplace/redeem_test.go b/marketplace/redeem_test.go index 73809d26c7..9bf6c8564d 100644 --- a/marketplace/redeem_test.go +++ b/marketplace/redeem_test.go @@ -317,7 +317,7 @@ func TestApplyDelegationRejectsBadParams(t *testing.T) { t.Run(name, func(t *testing.T) { h := newHandler() err := h.ApplyDelegation(context.Background(), &marketplace.RedemptionDelegationUpdate{ - ExpirationTime: time.Now().Add(time.Hour), + ExpirationTime: time.Now().Add(time.Hour).Unix(), IdLimitHigh: 1000, Key: tc.key, EncodingPoints: tc.points, @@ -335,7 +335,7 @@ func TestApplyDelegationAcceptsEveryAESKeySize(t *testing.T) { for _, size := range []int{16, 24, 32} { t.Run(fmt.Sprintf("%d bytes", size), func(t *testing.T) { _, err := marketplace.NewRedemptionService(&marketplace.RedemptionDelegationUpdate{ - ExpirationTime: time.Now().Add(time.Hour), + ExpirationTime: time.Now().Add(time.Hour).Unix(), IdLimitHigh: 1000, Key: make([]byte, size), EncodingPoints: completeEncodingPoints(), @@ -349,7 +349,7 @@ func TestApplyDelegationAcceptsEveryAESKeySize(t *testing.T) { // belongs to the type, not only to the callers that happen to validate. func TestNewRedemptionServiceRejectsIncompleteEncodingPoints(t *testing.T) { _, err := marketplace.NewRedemptionService(&marketplace.RedemptionDelegationUpdate{ - ExpirationTime: time.Now().Add(time.Hour), + ExpirationTime: time.Now().Add(time.Hour).Unix(), IdLimitHigh: 1000, Key: make([]byte, 16), EncodingPoints: []uint32{100, 200}, @@ -362,7 +362,7 @@ func TestNewRedemptionServiceRejectsIncompleteEncodingPoints(t *testing.T) { // so the checks above cannot pass merely by rejecting everything. func TestNewRedemptionServiceAcceptsACompleteTable(t *testing.T) { s, err := marketplace.NewRedemptionService(&marketplace.RedemptionDelegationUpdate{ - ExpirationTime: time.Now().Add(time.Hour), + ExpirationTime: time.Now().Add(time.Hour).Unix(), IdLimitHigh: 1000, Key: make([]byte, 16), EncodingPoints: completeEncodingPoints(), diff --git a/marketplace/redemption_service.go b/marketplace/redemption_service.go index 8934337a23..475320fb4a 100644 --- a/marketplace/redemption_service.go +++ b/marketplace/redemption_service.go @@ -40,10 +40,10 @@ type RedemptionService struct { encodingPoints []uint32 cipher cipher.Block resIdStore *UsedIDStore - expiration time.Time + expiration int64 } type RedemptionDelegationUpdate struct { - ExpirationTime time.Time + ExpirationTime int64 IdLimitLow uint32 IdLimitHigh uint32 Key []byte @@ -99,7 +99,7 @@ func (r *RedemptionService) Redeem( req *hummingbird.RedeemAssetFromASRequest, ) *hummingbird.RedeemAssetFromASResponse { now := time.Now() - resId, err := r.resIdStore.Next(now.Unix(), req.StartsAt.Seconds, req.StopsAt.Seconds) + resId, err := r.resIdStore.Next(now.Unix(), req.StartsAt, req.StopsAt) if err != nil { return &hummingbird.RedeemAssetFromASResponse{ Result: &hummingbird.RedeemAssetFromASResponse_Error{ @@ -108,13 +108,13 @@ func (r *RedemptionService) Redeem( } } - unixStart := uint32(req.StartsAt.Seconds) - unixEnd := uint32(req.StopsAt.Seconds) - durSeconds := unixEnd - unixStart + unixStart := req.StartsAt + unixEnd := req.StopsAt + durSeconds := uint32(unixEnd - unixStart) encoded_bw := r.encodeBandwidth(req.Bandwidth) var buff [16]byte - ak := hbird.DeriveAuthKey(r.cipher, resId, encoded_bw, uint16(req.IngressId), uint16(req.EgressId), unixStart, uint16(durSeconds), buff[:]) + ak := hbird.DeriveAuthKey(r.cipher, resId, encoded_bw, uint16(req.IngressId), uint16(req.EgressId), uint32(unixStart), uint16(durSeconds), buff[:]) return &hummingbird.RedeemAssetFromASResponse{ Result: &hummingbird.RedeemAssetFromASResponse_ResInfo{ ResInfo: &hummingbird.ReservationInfo{ diff --git a/marketplace/service.go b/marketplace/service.go index 93372beb22..26ead16a17 100644 --- a/marketplace/service.go +++ b/marketplace/service.go @@ -30,7 +30,6 @@ import ( "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/proto/hummingbird" - "google.golang.org/protobuf/types/known/timestamppb" ) type Service struct { @@ -163,11 +162,7 @@ func (s *Service) SplitAsset(ctx context.Context, req *connect.Request[hummingbi ids, err = s.store.SplitAsset(ctx, userId, assetId, bwSplit.Splits, nil) case *hummingbird.SplitAssetRequest_TimeSplit: splits := req.Msg.GetTimeSplit().Splits - times := make([]time.Time, 0, len(splits)) - for _, split := range splits { - times = append(times, split.AsTime()) - } - ids, err = s.store.SplitAsset(ctx, userId, assetId, nil, times) + ids, err = s.store.SplitAsset(ctx, userId, assetId, nil, splits) default: return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("invalid split request")) } @@ -213,16 +208,6 @@ func (s *Service) FetchReservations(ctx context.Context, req *connect.Request[hu if !ok { return nil, connect.NewError(connect.CodePermissionDenied, serrors.New("user_id not provided")) } - var startsAt *string - var stopsAt *string - if req.Msg.StartsAt != nil { - start := req.Msg.StartsAt.AsTime().UTC().Format(time.RFC3339) - startsAt = &start - } - if req.Msg.StopsAt != nil { - stop := req.Msg.StopsAt.AsTime().UTC().Format(time.RFC3339) - stopsAt = &stop - } var ia *addr.IA if req.Msg.Ia != nil { tmp := addr.IA(*req.Msg.Ia) @@ -232,8 +217,8 @@ func (s *Service) FetchReservations(ctx context.Context, req *connect.Request[hu IA: ia, Ingress: req.Msg.IngressId, Egress: req.Msg.EgressId, - StartsAt: startsAt, - StopsAt: stopsAt, + StartsAt: req.Msg.StartsAt, + StopsAt: req.Msg.StopsAt, Bandwidth: req.Msg.Bandwidth, AccountId: user, }) @@ -249,8 +234,8 @@ func (s *Service) FetchReservations(ctx context.Context, req *connect.Request[hu EgressId: reservation.Egress, Bandwidth: reservation.Bandwidth, DataplaneEncoding: uint32(reservation.EncodedBandwidth), - StartsAt: timestamppb.New(reservation.StartsAt), - StopsAt: timestamppb.New(reservation.StopsAt), + StartsAt: reservation.StartsAt, + StopsAt: reservation.StopsAt, AuthenticationKey: reservation.Key, }) } @@ -307,14 +292,14 @@ func (s *Service) UpdateAssets(ctx context.Context, req *connect.Request[humming Bandwidth: t.Update.Bandwidth, BandwidthMin: t.Update.BandwidthMin, BandwidthMax: t.Update.BandwidthMax, - StartAt: t.Update.StartsAt.AsTime(), - StopsAt: t.Update.StopsAt.AsTime(), + StartAt: t.Update.StartsAt, + StopsAt: t.Update.StopsAt, Price: t.Update.Price, TimeGranularity: t.Update.TimeGranularity, TimeMinDuration: t.Update.TimeMinDuration, TimeMaxDuration: t.Update.TimeMaxDuration, } - if dbAsset.StopsAt.After(time.Now().Add(time.Duration(s.info.AssetValidityMax) * time.Second)) { + if time.Unix(dbAsset.StopsAt, 0).UTC().After(time.Now().Add(time.Duration(s.info.AssetValidityMax) * time.Second).UTC()) { return 0, serrors.New("validity outside allowed range") } if t.Update.IfIdIngress != nil { @@ -372,8 +357,8 @@ func (s *Service) PublishAsset(ctx context.Context, req *connect.Request[humming Bandwidth: req.Msg.Asset.Bandwidth, BandwidthMin: req.Msg.Asset.BandwidthMin, BandwidthMax: req.Msg.Asset.BandwidthMax, - StartAt: req.Msg.Asset.StartsAt.AsTime().Truncate(time.Second), - StopsAt: req.Msg.Asset.StopsAt.AsTime().Truncate(time.Second), + StartAt: req.Msg.Asset.StartsAt, + StopsAt: req.Msg.Asset.StopsAt, Price: req.Msg.Asset.Price, TimeGranularity: req.Msg.Asset.TimeGranularity, TimeMinDuration: req.Msg.Asset.TimeMinDuration, @@ -381,10 +366,10 @@ func (s *Service) PublishAsset(ctx context.Context, req *connect.Request[humming IfIdIngress: sql.NullInt32{}, IfIdEgress: sql.NullInt32{}, } - if dbAsset.StopsAt.After(time.Now().Add(time.Duration(s.info.AssetValidityMax) * time.Second)) { + if time.Unix(dbAsset.StopsAt, 0).UTC().After(time.Now().Add(time.Duration(s.info.AssetValidityMax) * time.Second).UTC()) { return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("validity outside allowed range")) } - if !dbAsset.StopsAt.After(dbAsset.StartAt) { + if dbAsset.StopsAt <= dbAsset.StartAt { return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("stopsAt must come after startsAt")) } if req.Msg.Asset.IfIdIngress != nil { @@ -446,8 +431,8 @@ func (s *Service) RedeemAsset( var bw uint32 var ingressID uint32 var egressID uint32 - var startsAt time.Time - var stopsAt time.Time + var startsAt int64 + var stopsAt int64 var ia addr.IA if len(assets) == 1 { // interface pair @@ -466,16 +451,8 @@ func (s *Service) RedeemAsset( ingressID = uint32(ingressAsset.IfIdIngress.Int32) egressID = uint32(egressAsset.IfIdEgress.Int32) ia = ingressAsset.IA - if ingressAsset.StartAt.Before(egressAsset.StartAt) { - startsAt = egressAsset.StartAt - } else { - startsAt = ingressAsset.StartAt - } - if ingressAsset.StopsAt.Before(egressAsset.StopsAt) { - stopsAt = ingressAsset.StopsAt - } else { - stopsAt = egressAsset.StopsAt - } + startsAt = max(ingressAsset.StartAt, egressAsset.StartAt) + stopsAt = min(ingressAsset.StopsAt, egressAsset.StopsAt) } else { return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("invalid assets")) } @@ -496,9 +473,9 @@ func (s *Service) RedeemAsset( } return err } - if stopsAt.Before(startsAt) { + if stopsAt <= startsAt { return nil, connect.NewError(connect.CodeInvalidArgument, - serrors.Join(serrors.New("stops at before starts at"), undoRedemption())) + serrors.Join(serrors.New("invalid validity range"), undoRedemption())) } peer := s.FindRedemptionServerHandler(ia) // The deadline is derived from the request context: a client who disconnects releases @@ -509,8 +486,8 @@ func (s *Service) RedeemAsset( Bandwidth: bw, IngressId: ingressID, EgressId: egressID, - StartsAt: timestamppb.New(startsAt), - StopsAt: timestamppb.New(stopsAt), + StartsAt: startsAt, + StopsAt: stopsAt, }) if err != nil { return nil, connect.NewError(connect.CodeUnavailable, @@ -562,19 +539,19 @@ func (s *Service) RedeemAsset( // is preserved. Without that, windowEnd.Sub(windowStart) is negative and the interval // count with it, which makes the slices of the caller panic. func statisticsWindow( - start, end time.Time, - step, granularity time.Duration, -) (windowStart, windowEnd time.Time, numIntervals int) { - if end.Before(start) { + start, end int64, + step, granularity int64, +) (windowStart, windowEnd int64, numIntervals int64) { + if end < start { start, end = end, start } - windowStart = start.UTC().Truncate(granularity) - windowEnd = hbird.RoundUpTime(end.UTC(), granularity) + windowStart = start - (start % granularity) + windowEnd = hbird.RoundUpTime(time.Unix(end, 0).UTC(), time.Duration(granularity)*time.Second).Unix() if step <= 0 { // Only reachable with a granularity of 0, which the config does not allow. return windowStart, windowEnd, 0 } - return windowStart, windowEnd, int(windowEnd.Sub(windowStart) / step) + return windowStart, windowEnd, (windowEnd - windowStart) / step } // bandwidthUtilization is the share of the published bandwidth that was bought. @@ -594,41 +571,41 @@ func bandwidthUtilization(bought, published uint64) float64 { // window it overlaps, in kbps seconds, along with the revenue that bandwidth stands for. func bandwidthPerInterval( assets []*db.DBStat, - windowStart, windowEnd time.Time, - step time.Duration, - numIntervals int, + windowStart, windowEnd int64, + step int64, + numIntervals int64, ) (bandwidth, income []uint64) { bandwidth = make([]uint64, numIntervals) income = make([]uint64, numIntervals) for _, asset := range assets { start := asset.StartsAt stop := asset.StopsAt - if asset.StartsAt.Before(windowStart) { + if asset.StartsAt < windowStart { start = windowStart } - if asset.StopsAt.After(windowEnd) { + if asset.StopsAt > windowEnd { stop = windowEnd } - first := int(start.Sub(windowStart) / step) - last := int(stop.Sub(windowStart) / step) + first := int64((start - windowStart) / step) + last := int64((stop - windowStart) / step) if first < 0 || last > numIntervals { continue } - if stop.Equal(windowStart.Add(time.Duration(last) * step)) { + if stop == (windowStart + (last * step)) { last-- } for i := first; i <= last && i < numIntervals; i++ { - intervalStart := windowStart.Add(time.Duration(i) * step) - intervalEnd := intervalStart.Add(step) + intervalStart := windowStart + (i * step) + intervalEnd := intervalStart + step overlapStart := start overlapEnd := stop - if asset.StartsAt.Before(intervalStart) { + if asset.StartsAt < intervalStart { overlapStart = intervalStart } - if asset.StopsAt.After(intervalEnd) { + if asset.StopsAt > intervalEnd { overlapEnd = intervalEnd } - duration := uint64(overlapEnd.Sub(overlapStart).Seconds()) + duration := uint64(overlapEnd - overlapStart) bwTimesDuration := uint64(asset.Bandwidth) * duration bandwidth[i] += bwTimesDuration income[i] += bwTimesDuration * uint64(asset.Price) @@ -648,15 +625,15 @@ func (s *Service) Statistics( granularity := time.Duration(s.info.StatisticsTimeGranularity) * time.Second step := hbird.RoundUpDuration(time.Duration(req.Msg.Step)*time.Second, granularity) windowStart, windowEnd, numIntervals := statisticsWindow( - req.Msg.Start.AsTime(), req.Msg.End.AsTime(), step, granularity) + req.Msg.Start, req.Msg.End, int64(step), int64(granularity)) if numIntervals > 1024 { return nil, connect.NewError(connect.CodeResourceExhausted, serrors.New("too many intervals")) } publishedAssets, boughtAssets, err := s.store.Statistics(ctx, &db.StatisticsQuery{ IA: ia, - WindowStart: windowStart.Format(time.RFC3339), - WindowEnd: windowEnd.Format(time.RFC3339), + WindowStart: windowStart, + WindowEnd: windowEnd, Ingress: req.Msg.IfIdIngress, Egress: req.Msg.IfIdEgress, }) @@ -664,9 +641,9 @@ func (s *Service) Statistics( return nil, connect.NewError(connect.CodeInvalidArgument, err) } bwPublished, _ := bandwidthPerInterval( - publishedAssets, windowStart, windowEnd, step, numIntervals) + publishedAssets, windowStart, windowEnd, int64(step/time.Second), numIntervals) bwBought, income := bandwidthPerInterval( - boughtAssets, windowStart, windowEnd, step, numIntervals) + boughtAssets, windowStart, windowEnd, int64(step/time.Second), numIntervals) respEntries := make([]*hummingbird.StatisticsResponseEntry, numIntervals) for i := range numIntervals { @@ -724,29 +701,44 @@ func (s *Service) SearchAssets(ctx context.Context, req *connect.Request[humming return nil, connect.NewError(connect.CodeInvalidArgument, err) } repAssets := make([]*hummingbird.SearchAsset, 0, len(assets)) + assetMap := make(map[int64]*hummingbird.SearchAsset) for _, asset := range assets { - a := &hummingbird.SearchAsset{ - AssetId: protoAssetID(asset.ID), - Ia: uint64(asset.IA), - Bandwidth: asset.Bandwidth, - BandwidthMin: asset.BandwidthMin, - BandwidthMax: asset.BandwidthMax, - TimeMinDuration: asset.TimeMinDuration, - TimeMaxDuration: asset.TimeMaxDuration, - StartsAt: timestamppb.New(asset.StartAt), - StopsAt: timestamppb.New(asset.StopsAt), - TimeGranularity: asset.TimeGranularity, - Price: asset.Price, - } - if asset.IfIdIngress.Valid { - ingress := uint32(asset.IfIdIngress.Int32) - a.IfIdIngress = &ingress - } - if asset.IfIdEgress.Valid { - egress := uint32(asset.IfIdEgress.Int32) - a.IfIdEgress = &egress + mapAsset, found := assetMap[asset.ID] + if found { + mapAsset.Intervals = append(mapAsset.Intervals, &hummingbird.SearchAssetInterval{ + Bandwidth: asset.Bandwidth, + StartsAt: asset.StartAt, + StopsAt: asset.StopsAt, + }) + } else { + a := &hummingbird.SearchAsset{ + AssetId: protoAssetID(asset.ID), + Ia: uint64(asset.IA), + BandwidthMin: asset.BandwidthMin, + BandwidthMax: asset.BandwidthMax, + TimeMinDuration: asset.TimeMinDuration, + TimeMaxDuration: asset.TimeMaxDuration, + TimeGranularity: asset.TimeGranularity, + Price: asset.Price, + Intervals: []*hummingbird.SearchAssetInterval{ + &hummingbird.SearchAssetInterval{ + Bandwidth: asset.Bandwidth, + StartsAt: asset.StartAt, + StopsAt: asset.StopsAt, + }, + }, + } + if asset.IfIdIngress.Valid { + ingress := uint32(asset.IfIdIngress.Int32) + a.IfIdIngress = &ingress + } + if asset.IfIdEgress.Valid { + egress := uint32(asset.IfIdEgress.Int32) + a.IfIdEgress = &egress + } + repAssets = append(repAssets, a) + assetMap[asset.ID] = a } - repAssets = append(repAssets, a) } return &connect.Response[hummingbird.SearchAssetsResponse]{ Msg: &hummingbird.SearchAssetsResponse{ diff --git a/marketplace/statistics_test.go b/marketplace/statistics_test.go index 6427b87085..c0b251896a 100644 --- a/marketplace/statistics_test.go +++ b/marketplace/statistics_test.go @@ -126,10 +126,10 @@ func TestStatisticsWindow(t *testing.T) { func TestBandwidthPerIntervalGaps(t *testing.T) { // Two one-day assets, days 0 and 4 of a six day window. assets := []*db.DBStat{ - {Bandwidth: 100, Price: 2, StartsAt: statsBase, StopsAt: statsBase.Add(day)}, + {Bandwidth: 100, Price: 2, StartsAt: statsBase.Unix(), StopsAt: statsBase.Add(day).Unix()}, { Bandwidth: 100, Price: 2, - StartsAt: statsBase.Add(4 * day), StopsAt: statsBase.Add(5 * day), + StartsAt: statsBase.Add(4 * day).Unix(), StopsAt: statsBase.Add(5 * day).Unix(), }, } windowStart, windowEnd, n := marketplace.StatisticsWindow( @@ -171,7 +171,7 @@ func TestBandwidthPerIntervalEmptyWindow(t *testing.T) { // so that the extraction of the loop is covered for both of its outputs. func TestBandwidthPerIntervalRevenue(t *testing.T) { assets := []*db.DBStat{ - {Bandwidth: 100, Price: 3, StartsAt: statsBase, StopsAt: statsBase.Add(day)}, + {Bandwidth: 100, Price: 3, StartsAt: statsBase.Unix(), StopsAt: statsBase.Add(day).Unix()}, } windowStart, windowEnd, n := marketplace.StatisticsWindow( statsBase, statsBase.Add(day), day, day) diff --git a/marketplace/storage/BUILD.bazel b/marketplace/storage/BUILD.bazel index 6e29c21fdb..48986fb9c7 100644 --- a/marketplace/storage/BUILD.bazel +++ b/marketplace/storage/BUILD.bazel @@ -13,7 +13,6 @@ go_library( "//marketplace/db:go_default_library", "//pkg/addr:go_default_library", "//pkg/hummingbird:go_default_library", - "//pkg/hummingbird/marketplace:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/proto/hummingbird:go_default_library", "//private/config:go_default_library", diff --git a/marketplace/storage/split.go b/marketplace/storage/split.go index f12bc7bb81..617d7ca55e 100644 --- a/marketplace/storage/split.go +++ b/marketplace/storage/split.go @@ -16,18 +16,17 @@ package storage import ( "fmt" - "time" ) type RequestedSplit struct { - ExactFrom time.Time - ExactTo time.Time + ExactFrom int64 + ExactTo int64 ExactBandwidth uint32 } type AssetSegment struct { - StartsAt time.Time - StopsAt time.Time + StartsAt int64 + StopsAt int64 Bandwidth uint32 } @@ -37,30 +36,24 @@ type SplitResult struct { } func validateSplit(asset AssetSegment, p RequestedSplit) error { - if p.ExactFrom.Before(asset.StartsAt) || p.ExactTo.After(asset.StopsAt) { + if p.ExactFrom < asset.StartsAt || p.ExactTo > asset.StopsAt { return fmt.Errorf("split outside asset bounds") } - if !p.ExactFrom.Before(p.ExactTo) { + if p.ExactFrom >= p.ExactTo { return fmt.Errorf("invalid validity range") } - if p.ExactFrom.Nanosecond() != 0 || p.ExactTo.Nanosecond() != 0 { - return fmt.Errorf("timestamps must be second precision") - } if p.ExactBandwidth > asset.Bandwidth { return fmt.Errorf("split amount exceeds asset amount") } if p.ExactBandwidth == 0 { return fmt.Errorf("split cannot be zero") } - if p.ExactFrom.Equal(p.ExactTo) { - return fmt.Errorf("split cannot be zero") - } return nil } func SplitAsset(asset AssetSegment, split RequestedSplit) (*SplitResult, error) { - if !asset.StartsAt.Before(asset.StopsAt) { + if asset.StartsAt >= asset.StopsAt { return nil, fmt.Errorf("invalid asset range") } if err := validateSplit(asset, split); err != nil { @@ -80,9 +73,9 @@ func SplitAsset(asset AssetSegment, split RequestedSplit) (*SplitResult, error) }) remainingAsset.Bandwidth = split.ExactBandwidth } - if split.ExactFrom.Equal(remainingAsset.StartsAt) { + if split.ExactFrom == remainingAsset.StartsAt { // no left remainder asset exists - if split.ExactTo.Equal(remainingAsset.StopsAt) { + if split.ExactTo == remainingAsset.StopsAt { // no right remainder asset exists -> split = asset splitResult.Split = AssetSegment{ Bandwidth: remainingAsset.Bandwidth, @@ -104,7 +97,7 @@ func SplitAsset(asset AssetSegment, split RequestedSplit) (*SplitResult, error) } } else { // a left remainder asset exists - if split.ExactTo.Equal(remainingAsset.StopsAt) { + if split.ExactTo == remainingAsset.StopsAt { // no right remainder asset exists -> only a left remainder exists splitResult.Split = AssetSegment{ Bandwidth: remainingAsset.Bandwidth, diff --git a/marketplace/storage/split_test.go b/marketplace/storage/split_test.go index dcacd22beb..d8b7b72a1f 100644 --- a/marketplace/storage/split_test.go +++ b/marketplace/storage/split_test.go @@ -26,8 +26,8 @@ import ( // and what goes back on the market. The remainders must never overlap the // purchase, otherwise the same capacity could be sold twice. func TestSplitAsset(t *testing.T) { - start := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC) - stop := start.Add(time.Hour) + start := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC).Unix() + stop := start + 60*60 asset := func() AssetSegment { return AssetSegment{ Bandwidth: 1000, @@ -47,48 +47,48 @@ func TestSplitAsset(t *testing.T) { }, "tail, only a left remainder": { split: RequestedSplit{ - ExactFrom: start.Add(10 * time.Minute), + ExactFrom: start + (10 * 60), ExactTo: stop, ExactBandwidth: 1000, }, expected: AssetSegment{ - StartsAt: start.Add(10 * time.Minute), + StartsAt: start + (10 * 60), StopsAt: stop, Bandwidth: 1000, }, remainders: []AssetSegment{ - {StartsAt: start, StopsAt: start.Add(10 * time.Minute), Bandwidth: 1000}, + {StartsAt: start, StopsAt: start + (10 * 60), Bandwidth: 1000}, }, }, "head, only a right remainder": { split: RequestedSplit{ ExactFrom: start, - ExactTo: start.Add(10 * time.Minute), + ExactTo: start + (10 * 60), ExactBandwidth: 1000, }, expected: AssetSegment{ StartsAt: start, - StopsAt: start.Add(10 * time.Minute), + StopsAt: start + (10 * 60), Bandwidth: 1000, }, remainders: []AssetSegment{ - {StartsAt: start.Add(10 * time.Minute), StopsAt: stop, Bandwidth: 1000}, + {StartsAt: start + (10 * 60), StopsAt: stop, Bandwidth: 1000}, }, }, "middle, a left and a right remainder": { split: RequestedSplit{ - ExactFrom: start.Add(10 * time.Minute), - ExactTo: start.Add(20 * time.Minute), + ExactFrom: start + (10 * 60), + ExactTo: start + (20 * 60), ExactBandwidth: 1000, }, expected: AssetSegment{ - StartsAt: start.Add(10 * time.Minute), - StopsAt: start.Add(20 * time.Minute), + StartsAt: start + (10 * 60), + StopsAt: start + (20 * 60), Bandwidth: 1000, }, remainders: []AssetSegment{ - {StartsAt: start, StopsAt: start.Add(10 * time.Minute), Bandwidth: 1000}, - {StartsAt: start.Add(20 * time.Minute), StopsAt: stop, Bandwidth: 1000}, + {StartsAt: start, StopsAt: start + (10 * 60), Bandwidth: 1000}, + {StartsAt: start + (20 * 60), StopsAt: stop, Bandwidth: 1000}, }, }, "part of the bandwidth of the whole asset": { @@ -100,20 +100,20 @@ func TestSplitAsset(t *testing.T) { }, "part of the bandwidth of a middle slice": { split: RequestedSplit{ - ExactFrom: start.Add(10 * time.Minute), - ExactTo: start.Add(20 * time.Minute), + ExactFrom: start + (10 * 60), + ExactTo: start + (20 * 60), ExactBandwidth: 400, }, expected: AssetSegment{ - StartsAt: start.Add(10 * time.Minute), - StopsAt: start.Add(20 * time.Minute), + StartsAt: start + (10 * 60), + StopsAt: start + (20 * 60), Bandwidth: 400, }, remainders: []AssetSegment{ // The bandwidth that was not bought, for the whole window. {StartsAt: start, StopsAt: stop, Bandwidth: 600}, - {StartsAt: start, StopsAt: start.Add(10 * time.Minute), Bandwidth: 400}, - {StartsAt: start.Add(20 * time.Minute), StopsAt: stop, Bandwidth: 400}, + {StartsAt: start, StopsAt: start + (10 * 60), Bandwidth: 400}, + {StartsAt: start + (20 * 60), StopsAt: stop, Bandwidth: 400}, }, }, } @@ -128,8 +128,8 @@ func TestSplitAsset(t *testing.T) { // What is sold and what is left must add up to the asset, so no // remainder may overlap the purchase in both time and bandwidth. for _, remainder := range result.Remainders { - overlapsInTime := remainder.StartsAt.Before(result.Split.StopsAt) && - remainder.StopsAt.After(result.Split.StartsAt) + overlapsInTime := remainder.StartsAt < (result.Split.StopsAt) && + remainder.StopsAt > (result.Split.StartsAt) if !overlapsInTime { continue } @@ -144,16 +144,16 @@ func TestSplitAsset(t *testing.T) { // TestSplitAssetRejects checks that SplitAsset returns error when the asset cannot be split into // the required parts. func TestSplitAssetRejects(t *testing.T) { - start := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC) - stop := start.Add(time.Hour) + start := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC).Unix() + stop := start + 60*60 asset := AssetSegment{Bandwidth: 1000, StartsAt: start, StopsAt: stop} testCases := map[string]RequestedSplit{ "starts before the asset": { - ExactFrom: start.Add(-time.Second), ExactTo: stop, ExactBandwidth: 1000, + ExactFrom: start - 1, ExactTo: stop, ExactBandwidth: 1000, }, "stops after the asset": { - ExactFrom: start, ExactTo: stop.Add(time.Second), ExactBandwidth: 1000, + ExactFrom: start, ExactTo: stop + 1, ExactBandwidth: 1000, }, "empty range": { ExactFrom: start, ExactTo: start, ExactBandwidth: 1000, @@ -161,9 +161,6 @@ func TestSplitAssetRejects(t *testing.T) { "reversed range": { ExactFrom: stop, ExactTo: start, ExactBandwidth: 1000, }, - "sub second precision": { - ExactFrom: start.Add(time.Millisecond), ExactTo: stop, ExactBandwidth: 1000, - }, "more bandwidth than the asset has": { ExactFrom: start, ExactTo: stop, ExactBandwidth: 1001, }, diff --git a/marketplace/storage/storage.go b/marketplace/storage/storage.go index 27e3c1edfb..210be3112d 100644 --- a/marketplace/storage/storage.go +++ b/marketplace/storage/storage.go @@ -24,7 +24,6 @@ import ( marketplacedb "github.com/scionproto/scion/marketplace/db" "github.com/scionproto/scion/pkg/addr" hbird "github.com/scionproto/scion/pkg/hummingbird" - hbirdmarketplace "github.com/scionproto/scion/pkg/hummingbird/marketplace" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/proto/hummingbird" "github.com/scionproto/scion/private/config" @@ -105,7 +104,7 @@ func (s *MarketplaceStorage) PublishAsset( if err != nil { return err } - _, err = tx.InsertAssetSegment(ctx, assetId, a.StartAt.Unix(), a.StopsAt.Unix(), a.Bandwidth) + _, err = tx.InsertAssetSegment(ctx, assetId, a.StartAt, a.StopsAt, a.Bandwidth) if err != nil { return err } @@ -306,24 +305,24 @@ func (s *MarketplaceStorage) CreateOrUpdateRedemptionDelegations( if err != nil { return err } - paidUntil := time.Now() + paidUntil := time.Now().Unix() if delegation != nil { paidUntil = delegation.PaidUntil } if s.delegationHourlyFee == 0 { - if r.Expiration.After(paidUntil) { + if r.Expiration > paidUntil { r.PaidUntil = r.Expiration } else { r.PaidUntil = paidUntil } - } else if r.Expiration.After(paidUntil) { - paymentDuration := hbird.RoundUpDuration(r.Expiration.Sub(paidUntil), time.Hour) + } else if r.Expiration > paidUntil { + paymentDuration := hbird.RoundUpDuration(time.Duration(r.Expiration-paidUntil)*time.Second, time.Hour) numHours := uint64(paymentDuration / time.Hour) if _, err := tx.UpdateASMoney( ctx, r.IA, -int64(numHours*s.delegationHourlyFee)); err != nil { return err } - r.PaidUntil = paidUntil.Add(paymentDuration) + r.PaidUntil = paidUntil + int64(numHours)*60*60 } else { r.PaidUntil = paidUntil } @@ -448,7 +447,7 @@ func (s *MarketplaceStorage) CombineAssets( ingress := assets[0].IfIdIngress egress := assets[0].IfIdEgress combinedStop := assets[0].StopsAt - useTimeAxis := assets[0].StopsAt.Equal(assets[1].StartAt) + useTimeAxis := assets[0].StopsAt == assets[1].StartAt combinedMinBW := assets[0].BandwidthMin combinedMaxBW := assets[0].BandwidthMax combinedTimeMinDuration := assets[0].TimeMinDuration @@ -475,12 +474,12 @@ func (s *MarketplaceStorage) CombineAssets( if assets[0].Bandwidth != asset.Bandwidth { return serrors.New("assets cannot be combined") } - if !combinedStop.Equal(asset.StartAt) { + if combinedStop != asset.StartAt { return serrors.New("assets cannot be combined") } combinedStop = asset.StopsAt } else { - if !asset.StartAt.Equal(assets[0].StartAt) || !asset.StopsAt.Equal(assets[0].StopsAt) { + if asset.StartAt != assets[0].StartAt || asset.StopsAt != assets[0].StopsAt { return serrors.New("assets cannot be combined") } combinedBandwidth += asset.Bandwidth @@ -519,7 +518,7 @@ func (s *MarketplaceStorage) SplitAsset( accountID int64, assetId int64, bwSplit []uint32, - timeSplit []time.Time, + timeSplit []int64, ) ([]int64, error) { if len(bwSplit) == 0 && len(timeSplit) == 0 { return nil, serrors.New("invalid split request") @@ -580,7 +579,7 @@ func (s *MarketplaceStorage) SplitAsset( } } } - if baseSegment.Bandwidth != 0 && !baseSegment.StartsAt.Equal(baseSegment.StopsAt) { + if baseSegment.Bandwidth != 0 && baseSegment.StartsAt != baseSegment.StopsAt { splitSegments = append(splitSegments, baseSegment) } if err := tx.RemoveAsset(ctx, assetId); err != nil { @@ -645,7 +644,7 @@ func (s *MarketplaceStorage) BuyAssets( return nil, 0, serrors.New("no assets specified") } for _, asset := range assets { - if asset.BandwidthExact <= 0 || asset.StartsAtExactly.Seconds >= asset.StopsAtExactly.Seconds { + if asset.BandwidthExact <= 0 || asset.StartsAtExactly >= asset.StopsAtExactly { return nil, 0, serrors.New("invalid asset") } } @@ -653,7 +652,7 @@ func (s *MarketplaceStorage) BuyAssets( costAcc := int64(0) err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { calcCost := func(a *marketplacedb.DBAsset) (int64, bool) { - totalAssetPrice, ok := multiplyInts(int64(a.StopsAt.Sub(a.StartAt).Seconds()), + totalAssetPrice, ok := multiplyInts(int64(a.StopsAt-a.StartAt), int64(a.Bandwidth), int64(a.Price)) if !ok { return 0, false @@ -667,7 +666,7 @@ func (s *MarketplaceStorage) BuyAssets( if err != nil { return err } - newAsset, err := tx.BuyAsset(ctx, assetId, asset.StartsAtExactly.Seconds, asset.StopsAtExactly.Seconds, + newAsset, err := tx.BuyAsset(ctx, assetId, asset.StartsAtExactly, asset.StopsAtExactly, asset.BandwidthExact, accountID) if err != nil { return err @@ -695,138 +694,6 @@ func (s *MarketplaceStorage) BuyAssets( return boughtIDs, costAcc, nil } -func (s *MarketplaceStorage) BuyAssetsOld( - ctx context.Context, - accountID int64, - assets []*hummingbird.BuyAsset, - maxPrice uint64, -) ([]int64, int64, error) { - uniqueCheck := make(map[int64]bool) - for _, asset := range assets { - assetId, err := DatabaseAssetID(asset.AssetId) - if err != nil { - return nil, 0, err - } - if uniqueCheck[assetId] { - return nil, 0, serrors.New("Only a single split per asset per buy request allowed") - } - if asset.StopsAtExactly.AsTime().Before(asset.StartsAtExactly.AsTime()) { - return nil, 0, serrors.New("End of validity must come after start of validity") - } - uniqueCheck[assetId] = true - } - boughtAssets := make([]int64, 0, 1) - costAcc := int64(0) - err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { - for _, asset := range assets { - assetId, err := DatabaseAssetID(asset.AssetId) - if err != nil { - return err - } - dbAsset, err := tx.TransitionAsset(ctx, assetId, nil, - marketplacedb.AssetStateAvailable, marketplacedb.AssetStateCheckedOut) - if err != nil { - return err - } - baseSegment := AssetSegment{ - StartsAt: dbAsset.StartAt, - StopsAt: dbAsset.StopsAt, - Bandwidth: dbAsset.Bandwidth, - } - split, err := SplitAsset(baseSegment, RequestedSplit{ - ExactFrom: asset.StartsAtExactly.AsTime(), - ExactTo: asset.StopsAtExactly.AsTime(), - ExactBandwidth: asset.BandwidthExact, - }) - if err != nil { - return err - } - newAsset := &marketplacedb.DBAsset{ - AccountId: sql.NullInt64{Int64: accountID, Valid: true}, - IA: dbAsset.IA, - BandwidthMin: dbAsset.BandwidthMin, - BandwidthMax: dbAsset.BandwidthMax, - TimeGranularity: dbAsset.TimeGranularity, - TimeMinDuration: dbAsset.TimeMinDuration, - TimeMaxDuration: dbAsset.TimeMaxDuration, - IfIdIngress: dbAsset.IfIdIngress, - IfIdEgress: dbAsset.IfIdEgress, - Bandwidth: split.Split.Bandwidth, - StartAt: split.Split.StartsAt, - StopsAt: split.Split.StopsAt, - Price: dbAsset.Price, - } - id, err := tx.InsertAsset(ctx, newAsset) - if err != nil { - return err - } - newAsset.ID = id - _, err = tx.RegisterAssetEvent(ctx, newAsset, marketplacedb.AssetBought) - if err != nil { - return err - } - totalAssetPrice, err := hbirdmarketplace.ReservationPrice( - dbAsset.Price, - split.Split.Bandwidth, - dbAsset.BandwidthMin, - dbAsset.TimeMinDuration, - dbAsset.TimeGranularity, - split.Split.StopsAt.Sub(split.Split.StartsAt)) - if err != nil { - return serrors.Wrap("calculating total asset price", err) - } - if totalAssetPrice > math.MaxInt64 { - return serrors.New("total asset price would lead to integer overflow") - } - fee := int64(float64(totalAssetPrice)*float64(s.transactionFeeRelative)) + - int64(s.transactionFeeAbsolute) - pricePlusFee, safe := addInt64(int64(totalAssetPrice), fee) - if !safe { - return serrors.New("total asset price would lead to integer overflow") - } - if _, err := tx.UpdateASMoney(ctx, dbAsset.IA, int64(totalAssetPrice)); err != nil { - return err - } - costAcc, safe = addInt64(costAcc, pricePlusFee) - if !safe { - return serrors.New("total asset price would lead to integer overflow") - } - boughtAssets = append(boughtAssets, id) - for _, segment := range split.Remainders { - _, err := tx.InsertAsset(ctx, &marketplacedb.DBAsset{ - IA: dbAsset.IA, - BandwidthMin: dbAsset.BandwidthMin, - BandwidthMax: dbAsset.BandwidthMax, - TimeGranularity: dbAsset.TimeGranularity, - TimeMinDuration: dbAsset.TimeMinDuration, - TimeMaxDuration: dbAsset.TimeMaxDuration, - IfIdIngress: dbAsset.IfIdIngress, - IfIdEgress: dbAsset.IfIdEgress, - Bandwidth: segment.Bandwidth, - StartAt: segment.StartsAt, - StopsAt: segment.StopsAt, - Price: dbAsset.Price, - }) - if err != nil { - return err - } - } - if err := tx.RemoveAsset(ctx, dbAsset.ID); err != nil { - return err - } - } - if uint64(costAcc) > maxPrice { - return serrors.New("cost higher than max price") - } - _, err := tx.UpdateAccountMoney(ctx, accountID, -costAcc) - return err - }) - if err != nil { - return nil, 0, err - } - return boughtAssets, costAcc, nil -} - func (s *MarketplaceStorage) InsertReservation( ctx context.Context, r *marketplacedb.DBReservation, @@ -863,7 +730,7 @@ func (s *MarketplaceStorage) UndoRedemption( func validateAsset( a *marketplacedb.DBAsset, ) error { - duration := a.StopsAt.Sub(a.StartAt) + duration := a.StopsAt - a.StartAt if a.Bandwidth == 0 { return serrors.New("bandwidth is 0") } @@ -875,7 +742,7 @@ func validateAsset( func validateAssetForRedemption( a *marketplacedb.DBAsset, ) error { - duration := a.StopsAt.Sub(a.StartAt) + duration := a.StopsAt - a.StartAt if err := validateAsset(a); err != nil { return err } @@ -885,13 +752,13 @@ func validateAssetForRedemption( if a.Bandwidth > a.BandwidthMax { return serrors.New("bandwith > max_bandiwdth") } - if duration < time.Duration(a.TimeMinDuration)*time.Second { + if duration < int64(a.TimeMinDuration) { return serrors.New("duration < time_min_duration") } - if duration > time.Duration(a.TimeMaxDuration)*time.Second { + if duration > int64(a.TimeMaxDuration) { return serrors.New("duration > time_max_duration") } - if duration%(time.Duration(a.TimeGranularity)*time.Second) != 0 { + if duration%int64(a.TimeGranularity) != 0 { return serrors.New("duration not multiple of time granularity") } return nil diff --git a/marketplace_client/BUILD.bazel b/marketplace_client/BUILD.bazel index cd58fcf472..4a5ad0e6bc 100644 --- a/marketplace_client/BUILD.bazel +++ b/marketplace_client/BUILD.bazel @@ -17,7 +17,6 @@ go_library( "@com_connectrpc_connect//:go_default_library", "@com_github_golang_jwt_jwt//:go_default_library", "@com_github_scionproto_scion//pkg/proto/hummingbird/v1/hummingbirdconnect:go_default_library", - "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", ], ) diff --git a/marketplace_client/main.go b/marketplace_client/main.go index 1c7e507d4c..3404c98d28 100644 --- a/marketplace_client/main.go +++ b/marketplace_client/main.go @@ -40,7 +40,6 @@ import ( "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/proto/hummingbird" "github.com/scionproto/scion/pkg/proto/hummingbird/v1/hummingbirdconnect" - "google.golang.org/protobuf/types/known/timestamppb" ) type jwtType int @@ -387,7 +386,7 @@ func handleDelegate( resp, err := c.DelegateRedemption(ctx, &connect.Request[hummingbird.DelegateRedemptionRequest]{ Msg: &hummingbird.DelegateRedemptionRequest{ - ExpirationTime: timestamppb.New(expTime), + ExpirationTime: expTime.Unix(), ReservationIdLowerBound: idLowerBound, ReservationIdUpperBound: idUpperBound, Key: key, @@ -398,7 +397,7 @@ func handleDelegate( fmt.Println(err) return } - fmt.Println("Delegated until:", resp.Msg.ExpirationTime.AsTime()) + fmt.Println("Delegated until:", time.Unix(resp.Msg.ExpirationTime, 0)) } func handlePublish( ctx context.Context, @@ -412,8 +411,8 @@ func handlePublish( Bandwidth: uint32(readUint64(reader, "bandwidth: ")), BandwidthMin: uint32(readUint64(reader, "minimum bandwidth: ")), BandwidthMax: uint32(readUint64(reader, "maximum bandwidth: ")), - StartsAt: timestamppb.New(readTime(reader, "Starts at (2026-06-23T13:25:36Z): ")), - StopsAt: timestamppb.New(readTime(reader, "Stops at (2026-06-23T13:25:36Z): ")), + StartsAt: readTime(reader, "Starts at (2026-06-23T13:25:36Z): ").Unix(), + StopsAt: readTime(reader, "Stops at (2026-06-23T13:25:36Z): ").Unix(), Price: uint32(readUint64(reader, "price per kbit per second: ")), TimeMinDuration: uint32(readUint64(reader, "minimum time duration: ")), TimeMaxDuration: uint32(readUint64(reader, "maximum time duration: ")), @@ -494,10 +493,8 @@ func handleUpdate( Bandwidth: uint32(readUint64(reader, "bandwidth: ")), BandwidthMin: uint32(readUint64(reader, "minimum bandwidth: ")), BandwidthMax: uint32(readUint64(reader, "maximum bandwidth: ")), - StartsAt: timestamppb.New( - readTime(reader, "Starts at (2026-06-23T13:25:36Z): ")), - StopsAt: timestamppb.New( - readTime(reader, "Stops at (2026-06-23T13:25:36Z): ")), + StartsAt: readTime(reader, "Starts at (2026-06-23T13:25:36Z): ").Unix(), + StopsAt: readTime(reader, "Stops at (2026-06-23T13:25:36Z): ").Unix(), Price: uint32(readUint64( reader, "price per kbit per second: ")), TimeMinDuration: uint32(readUint64(reader, "minimum time duration: ")), @@ -586,8 +583,8 @@ func handleStatistics( } resp, err := c.Statistics(ctx, &connect.Request[hummingbird.StatisticsRequest]{ Msg: &hummingbird.StatisticsRequest{ - Start: timestamppb.New(startsAt), - End: timestamppb.New(stopsAt), + Start: startsAt.Unix(), + End: stopsAt.Unix(), Step: uint32(stepSize), IfIdIngress: ingress, IfIdEgress: egress, @@ -647,14 +644,14 @@ func handleSplit( case "time": timeSplits := readString(reader, "time split (2026-06-23T13:25:36Z): ") allSplits := strings.Split(timeSplits, ",") - timestampSplits := make([]*timestamppb.Timestamp, 0, len(allSplits)) + timestampSplits := make([]int64, 0, len(allSplits)) for _, split := range allSplits { timeSplit, err := time.Parse(time.RFC3339, split) if err != nil { fmt.Println(err) return } - timestampSplits = append(timestampSplits, timestamppb.New(timeSplit)) + timestampSplits = append(timestampSplits, timeSplit.Unix()) } req.SplitOption = &hummingbird.SplitAssetRequest_TimeSplit{ TimeSplit: &hummingbird.SplitTimeOption{ @@ -738,10 +735,12 @@ func handleReservation( Bandwidth: bw, } if startsAt != nil { - req.StartsAt = timestamppb.New(*startsAt) + tmp := startsAt.Unix() + req.StartsAt = &tmp } if stopsAt != nil { - req.StopsAt = timestamppb.New(*stopsAt) + tmp := stopsAt.Unix() + req.StopsAt = &tmp } rep, err := c.FetchReservations(ctx, &connect.Request[hummingbird.FetchReservationsRequest]{ Msg: req, @@ -770,8 +769,8 @@ func handleReservation( EgressId: res.EgressId, Bw: res.Bandwidth, EncodedBw: uint16(res.DataplaneEncoding), - StartsAt: res.StartsAt.AsTime(), - StopsAt: res.StopsAt.AsTime(), + StartsAt: time.Unix(res.StartsAt, 0), + StopsAt: time.Unix(res.StopsAt, 0), Ak: res.AuthenticationKey, }) } @@ -885,10 +884,10 @@ func handleBuy( fmt.Println("read asset id ", assetId, " with len ", len(assetId)) buyAsset := &hummingbird.BuyAsset{ AssetId: assetId, - StartsAtExactly: timestamppb.New(readTime(reader, - "Starts at exactly (2026-06-23T13:25:36Z): ")), - StopsAtExactly: timestamppb.New(readTime(reader, - "Stops at exactly (2026-06-23T13:25:36Z): ")), + StartsAtExactly: readTime(reader, + "Starts at exactly (2026-06-23T13:25:36Z): ").Unix(), + StopsAtExactly: readTime(reader, + "Stops at exactly (2026-06-23T13:25:36Z): ").Unix(), BandwidthExact: uint32(readUint64(reader, "BW exact: ")), } buyAssets = append(buyAssets, buyAsset) @@ -1003,6 +1002,7 @@ func handleSearch( } if minReqBw == nil { fmt.Println("bandwith required") + return } msg = &hummingbird.SearchAssetsRequest{ Request: &hummingbird.SearchAssetsRequest_Listed{ @@ -1028,20 +1028,26 @@ func handleSearch( } transformed := make([]Asset, 0, len(rep.Msg.Assets)) for _, asset := range rep.Msg.Assets { + intervals := []AssetInterval{} + for _, interval := range asset.Intervals { + intervals = append(intervals, AssetInterval{ + Bandwidth: interval.Bandwidth, + StartAt: time.Unix(interval.StartsAt, 0), + StopsAt: time.Unix(interval.StopsAt, 0), + }) + } transformed = append(transformed, Asset{ ID: hex.EncodeToString(asset.AssetId), IA: addr.IA(asset.Ia), - Bandwidth: asset.Bandwidth, BandwidthMin: asset.BandwidthMin, BandwidthMax: asset.BandwidthMax, TimeMinDuration: asset.TimeMinDuration, TimeMaxDuration: asset.TimeMaxDuration, - StartAt: asset.StartsAt.AsTime(), - StopsAt: asset.StopsAt.AsTime(), Price: asset.Price, IfIdIngress: asset.IfIdIngress, IfIdEgress: asset.IfIdEgress, TimeGranularity: asset.TimeGranularity, + Intervals: intervals, }) } j, err := json.MarshalIndent(transformed, "", "\t") @@ -1252,15 +1258,18 @@ func main() { type Asset struct { ID string IA addr.IA - Bandwidth uint32 BandwidthMin uint32 BandwidthMax uint32 - StartAt time.Time - StopsAt time.Time Price uint32 TimeGranularity uint32 TimeMinDuration uint32 TimeMaxDuration uint32 IfIdIngress *uint32 IfIdEgress *uint32 + Intervals []AssetInterval +} +type AssetInterval struct { + Bandwidth uint32 + StartAt time.Time + StopsAt time.Time } diff --git a/pkg/hummingbird/marketplace/BUILD.bazel b/pkg/hummingbird/marketplace/BUILD.bazel index 453a4c1919..e9664ed4d2 100644 --- a/pkg/hummingbird/marketplace/BUILD.bazel +++ b/pkg/hummingbird/marketplace/BUILD.bazel @@ -28,7 +28,6 @@ go_library( "@com_github_quic_go_quic_go//:go_default_library", "@com_github_quic_go_quic_go//http3:go_default_library", "@com_github_scionproto_scion//pkg/proto/hummingbird/v1/hummingbirdconnect:go_default_library", - "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", ], ) diff --git a/pkg/hummingbird/marketplace/client.go b/pkg/hummingbird/marketplace/client.go index 5d15801ca4..de8c59ec2f 100644 --- a/pkg/hummingbird/marketplace/client.go +++ b/pkg/hummingbird/marketplace/client.go @@ -20,6 +20,7 @@ import ( "fmt" "math" "sort" + "strconv" "sync" "time" @@ -30,7 +31,6 @@ import ( "github.com/scionproto/scion/pkg/proto/hummingbird" "github.com/scionproto/scion/pkg/snet" snetpath "github.com/scionproto/scion/pkg/snet/path" - "google.golang.org/protobuf/types/known/timestamppb" ) type BuyMode int @@ -63,13 +63,13 @@ func (c *Client) findExistingReservations( ctx context.Context, pairs []InterfacePair, bwInKbps uint32, - startsAt time.Time, - stopsAt time.Time, + startsAt int64, + stopsAt int64, ) ([]*snetpath.Hop, error) { req := &hummingbird.FetchReservationsRequest{ Bandwidth: &bwInKbps, - StartsAt: timestamppb.New(startsAt), - StopsAt: timestamppb.New(stopsAt), + StartsAt: &startsAt, + StopsAt: &stopsAt, } res, err := c.Marketplace.FetchReservations( ctx, &connect.Request[hummingbird.FetchReservationsRequest]{ @@ -92,8 +92,8 @@ func (c *Client) findExistingReservations( ResID: r.ReservationId, Ak: [16]byte(r.AuthenticationKey), Bw: uint16(r.DataplaneEncoding), - StartTime: uint32(r.StartsAt.Seconds), - Duration: uint16(r.StopsAt.Seconds - r.StartsAt.Seconds), + StartTime: uint32(r.StartsAt), + Duration: uint16(r.StopsAt - r.StartsAt), }, } break @@ -131,20 +131,40 @@ func timeMin(a time.Time, b time.Time) time.Time { return b } +type UnwrappedAsset struct { + AssetId []byte + Ia uint64 + IfIdIngress *uint32 + IfIdEgress *uint32 + BandwidthMin uint32 + BandwidthMax uint32 + TimeGranularity uint32 + TimeMinDuration uint32 + TimeMaxDuration uint32 + Price uint32 + StartsAt int64 + StopsAt int64 + Bandwidth uint32 +} + +func (a *UnwrappedAsset) UniqueID() string { + return strconv.Itoa(len(a.AssetId)) + string(a.AssetId) + strconv.Itoa(int(a.StartsAt)) +} + func activeAt( - elements []*hummingbird.SearchAsset, - t time.Time, + elements []*UnwrappedAsset, + t int64, bw uint32, -) []*hummingbird.SearchAsset { +) []*UnwrappedAsset { // Find first element with StartsAt > t end := sort.Search(len(elements), func(i int) bool { - return elements[i].StartsAt.AsTime().After(t) + return elements[i].StartsAt > t }) - result := make([]*hummingbird.SearchAsset, 0) + result := make([]*UnwrappedAsset, 0) for i := 0; i < end; i++ { - if t.Before(elements[i].StopsAt.AsTime()) && elements[i].Bandwidth >= bw { + if t < (elements[i].StopsAt) && elements[i].Bandwidth >= bw { result = append(result, elements[i]) } } @@ -153,24 +173,24 @@ func activeAt( // recursiveSelect func (c *Client) recursiveSelectStart( - assets []*hummingbird.SearchAsset, + assets []*UnwrappedAsset, bwInKbps uint32, - startsAt time.Time, - stopsAt time.Time, + startsAt int64, + stopsAt int64, combineCost uint64, ) ([]*hummingbird.BuyAsset, uint64, bool) { - sort.Slice(assets, func(i, j int) bool { - return assets[i].StartsAt.Seconds < assets[j].StartsAt.Seconds + return assets[i].StartsAt < assets[j].StartsAt }) - assetMap := make(map[string]*hummingbird.SearchAsset) + + assetMap := make(map[string]*UnwrappedAsset) for _, asset := range assets { - assetMap[string(asset.AssetId)] = asset + assetMap[asset.UniqueID()] = asset } filterValidAt := func( - t time.Time, - assets []*hummingbird.SearchAsset, - ) []*hummingbird.SearchAsset { + t int64, + assets []*UnwrappedAsset, + ) []*UnwrappedAsset { return activeAt(assets, t, bwInKbps) } @@ -188,14 +208,14 @@ func (c *Client) recursiveSelectStart( allChains = append(allChains, resultChain) } } - actualPrice := func(a *hummingbird.SearchAsset, duration time.Duration) uint64 { + actualPrice := func(a *UnwrappedAsset, durationInSeconds int64) uint64 { price, err := ReservationPrice( a.Price, bwInKbps, a.BandwidthMin, a.TimeMinDuration, a.TimeGranularity, - duration) + time.Duration(durationInSeconds)*time.Second) if err != nil { return math.MaxUint64 } @@ -208,11 +228,11 @@ func (c *Client) recursiveSelectStart( currSelect := []*hummingbird.BuyAsset{} if len(chain.ids) == 1 { asset := assetMap[string(chain.ids[0])] - currCost += actualPrice(asset, chain.stopsAt.Sub(startsAt)) + currCost += actualPrice(asset, chain.stopsAt-startsAt) currSelect = append(currSelect, &hummingbird.BuyAsset{ AssetId: asset.AssetId, - StartsAtExactly: timestamppb.New(startsAt), - StopsAtExactly: timestamppb.New(chain.stopsAt), + StartsAtExactly: startsAt, + StopsAtExactly: chain.stopsAt, BandwidthExact: chain.bandwidthMin, }) } else { @@ -223,48 +243,38 @@ func (c *Client) recursiveSelectStart( currAsset := assetMap[string(chain.ids[i])] nextAsset := assetMap[string(chain.ids[i+1])] if currAsset.Price <= nextAsset.Price { - duration := timeMin( - currAsset.StopsAt.AsTime(), - chain.stopsAt).Sub(currStartsAt) + duration := min(currAsset.StopsAt, chain.stopsAt) - currStartsAt currCost += actualPrice(currAsset, duration) currSelect = append(currSelect, &hummingbird.BuyAsset{ AssetId: currAsset.AssetId, - StartsAtExactly: timestamppb.New(currStartsAt), - StopsAtExactly: timestamppb.New(timeMin( - currAsset.StopsAt.AsTime(), - chain.stopsAt)), - BandwidthExact: chain.bandwidthMin, + StartsAtExactly: currStartsAt, + StopsAtExactly: min(currAsset.StopsAt, chain.stopsAt), + BandwidthExact: chain.bandwidthMin, }) - currStartsAt = timeMin(currAsset.StopsAt.AsTime(), chain.stopsAt) + currStartsAt = min(currAsset.StopsAt, chain.stopsAt) } else { - duration := timeMin( - nextAsset.StartsAt.AsTime(), - chain.stopsAt).Sub(currStartsAt) + duration := min(nextAsset.StartsAt, chain.stopsAt) - currStartsAt currCost += actualPrice(currAsset, duration) currSelect = append(currSelect, &hummingbird.BuyAsset{ AssetId: currAsset.AssetId, - StartsAtExactly: timestamppb.New(currStartsAt), - StopsAtExactly: timestamppb.New(timeMin( - nextAsset.StartsAt.AsTime(), - chain.stopsAt)), - BandwidthExact: chain.bandwidthMin, + StartsAtExactly: currStartsAt, + StopsAtExactly: min(nextAsset.StartsAt, chain.stopsAt), + BandwidthExact: chain.bandwidthMin, }) - currStartsAt = timeMin(nextAsset.StartsAt.AsTime(), chain.stopsAt) + currStartsAt = min(nextAsset.StartsAt, chain.stopsAt) } } lastAsset := assetMap[string(chain.ids[i])] - duration := timeMin(lastAsset.StopsAt.AsTime(), chain.stopsAt).Sub(currStartsAt) + duration := min(lastAsset.StopsAt, chain.stopsAt) - (currStartsAt) currCost += actualPrice(lastAsset, duration) currSelect = append(currSelect, &hummingbird.BuyAsset{ AssetId: lastAsset.AssetId, - StartsAtExactly: timestamppb.New(currStartsAt), - StopsAtExactly: timestamppb.New(timeMin( - lastAsset.StopsAt.AsTime(), - chain.stopsAt)), - BandwidthExact: chain.bandwidthMin, + StartsAtExactly: currStartsAt, + StopsAtExactly: min(lastAsset.StopsAt, chain.stopsAt), + BandwidthExact: chain.bandwidthMin, }) } if currCost < bestCost { @@ -277,51 +287,51 @@ func (c *Client) recursiveSelectStart( } type chain struct { - ids [][]byte + ids []string timeGranularity uint32 timeMinDuration uint32 bandwidthMin uint32 - stopsAt time.Time + stopsAt int64 } func (c *Client) recursiveSelect( - currentAsset *hummingbird.SearchAsset, - otherAssets []*hummingbird.SearchAsset, + currentAsset *UnwrappedAsset, + otherAssets []*UnwrappedAsset, previousBw uint32, previousTimeMinDuration uint32, previousTimeGranularity uint32, - globalStart time.Time, - globalStop time.Time, + globalStart int64, + globalStop int64, ) ([]chain, bool) { currBW := max(previousBw, currentAsset.BandwidthMin) timeGranularity := lcm(previousTimeGranularity, currentAsset.TimeGranularity) timeMinDuration := max(previousTimeMinDuration, currentAsset.TimeMinDuration) filterValidAt := func( - t time.Time, - assets []*hummingbird.SearchAsset, - ) []*hummingbird.SearchAsset { + t int64, + assets []*UnwrappedAsset, + ) []*UnwrappedAsset { return activeAt(assets, t, currBW) } - remainder := uint32(globalStop.Sub(globalStart).Seconds()) % timeGranularity + remainder := uint32(globalStop-globalStart) % timeGranularity if remainder != 0 { addedDuration := timeGranularity - remainder - globalStop = globalStop.Add(time.Duration(addedDuration) * time.Second) + globalStop = globalStop + int64(addedDuration) } - if uint32(globalStop.Sub(globalStart).Seconds()) < timeMinDuration { - globalStop = globalStart.Add(time.Duration(timeMinDuration) * time.Second) + if uint32(globalStop-globalStart) < timeMinDuration { + globalStop = globalStart + int64(timeMinDuration) } - if !currentAsset.StopsAt.AsTime().Before(globalStop) { + if currentAsset.StopsAt >= globalStop { // with this asset we found a chain from start till end return []chain{{ - ids: [][]byte{currentAsset.AssetId}, + ids: []string{currentAsset.UniqueID()}, bandwidthMin: currBW, timeGranularity: timeGranularity, timeMinDuration: timeMinDuration, stopsAt: globalStop}}, true } - nextAssets := filterValidAt(currentAsset.StopsAt.AsTime(), otherAssets) + nextAssets := filterValidAt(currentAsset.StopsAt, otherAssets) if len(nextAssets) == 0 { - fmt.Println("no assets found to be valid at ", currentAsset.StopsAt.AsTime()) + fmt.Println("no assets found to be valid at ", currentAsset.StopsAt) return nil, false } allChains := []chain{} @@ -343,7 +353,7 @@ func (c *Client) recursiveSelect( fmt.Println("bandwidth cannot be satisfied") continue } - resultChain.ids = append([][]byte{currentAsset.AssetId}, resultChain.ids...) + resultChain.ids = append([]string{currentAsset.UniqueID()}, resultChain.ids...) allChains = append(allChains, resultChain) } } @@ -352,9 +362,7 @@ func (c *Client) recursiveSelect( } func (c *Client) searchAllAssets(ctx context.Context, owned bool, ia *uint64, ingress *uint32, egress *uint32, - minBW uint32, startsAtLatest time.Time, stopsAtLatest time.Time, partialOverlap bool) ([]*hummingbird.SearchAsset, error) { - startsAtUnix := startsAtLatest.Unix() - stopsAtUnix := stopsAtLatest.Unix() + minBW uint32, startsAtLatest int64, stopsAtLatest int64, partialOverlap bool) ([]*UnwrappedAsset, error) { validityMode := hummingbird.ValidityMode_full_overlap if partialOverlap { validityMode = hummingbird.ValidityMode_partial_overlap @@ -370,8 +378,8 @@ func (c *Client) searchAllAssets(ctx context.Context, owned bool, ia *uint64, in IfIdIngress: ingress, IfIdEgress: egress, MinRequiredBw: &minBW, - StartsAtLatest: &startsAtUnix, - StopsAtEarliest: &stopsAtUnix, + StartsAtLatest: &startsAtLatest, + StopsAtEarliest: &stopsAtLatest, }, }, ValidityMode: validityMode, @@ -386,20 +394,40 @@ func (c *Client) searchAllAssets(ctx context.Context, owned bool, ia *uint64, in IfIdIngress: ingress, IfIdEgress: egress, MinRequiredBw: minBW, - StartsAtLatest: startsAtUnix, - StopsAtEarliest: stopsAtUnix, + StartsAtLatest: startsAtLatest, + StopsAtEarliest: stopsAtLatest, }, }, ValidityMode: validityMode, }, }) } - return resp.Msg.Assets, err + assets := []*UnwrappedAsset{} + for _, asset := range resp.Msg.Assets { + for _, interval := range asset.Intervals { + assets = append(assets, &UnwrappedAsset{ + AssetId: asset.AssetId, + Ia: asset.Ia, + IfIdIngress: asset.IfIdIngress, + IfIdEgress: asset.IfIdEgress, + BandwidthMin: asset.BandwidthMin, + BandwidthMax: asset.BandwidthMax, + TimeGranularity: asset.TimeGranularity, + TimeMinDuration: asset.TimeMinDuration, + TimeMaxDuration: asset.TimeMaxDuration, + Price: asset.Price, + StartsAt: interval.StartsAt, + StopsAt: interval.StopsAt, + Bandwidth: interval.Bandwidth, + }) + } + } + return assets, err } // checkoutAssetForInterfacePairWithCombine tries to find assets that can be combined on the time axis. It does not check for combinations on the bandwidth axis. func (c *Client) checkoutAssetForInterfacePairWithCombine(ctx context.Context, pair InterfacePair, bwInKbps uint32, - startsAt time.Time, stopsAt time.Time, combineCost uint64) ([]*hummingbird.BuyAsset, error) { + startsAt int64, stopsAt int64, combineCost uint64) ([]*hummingbird.BuyAsset, error) { ingressAndEgressSuccess := false var ingressBuyAssets, egressBuyAssets, pairBuyAssets []*hummingbird.BuyAsset @@ -409,7 +437,7 @@ func (c *Client) checkoutAssetForInterfacePairWithCombine(ctx context.Context, p if err != nil { return nil, err } - ingressSearchAssets := filter(ingressAssetsResponse, func(a *hummingbird.SearchAsset) bool { + ingressSearchAssets := filter(ingressAssetsResponse, func(a *UnwrappedAsset) bool { return a.IfIdEgress == nil }) ingressBuyAssets, ingressAssetPrice, ok = c.recursiveSelectStart(ingressSearchAssets, bwInKbps, startsAt, stopsAt, combineCost) @@ -418,7 +446,7 @@ func (c *Client) checkoutAssetForInterfacePairWithCombine(ctx context.Context, p if err != nil { return nil, err } - egressSearchAssets := filter(egressAssetsResponse, func(a *hummingbird.SearchAsset) bool { + egressSearchAssets := filter(egressAssetsResponse, func(a *UnwrappedAsset) bool { return a.IfIdIngress == nil }) egressBuyAssets, egressAssetPrice, ok = c.recursiveSelectStart(egressSearchAssets, bwInKbps, startsAt, stopsAt, combineCost) @@ -459,15 +487,15 @@ func (c *Client) checkoutAssetForInterfacePair( ctx context.Context, pair InterfacePair, bwInKbps uint32, - startsAt time.Time, - stopsAt time.Time, + startsAt int64, + stopsAt int64, ) ([]*hummingbird.BuyAsset, error) { - var ingressSearchAssets, egressSearchAssets, pairSearchAssets []*hummingbird.SearchAsset + var ingressSearchAssets, egressSearchAssets, pairSearchAssets []*UnwrappedAsset ingressAssetsResponse, err := c.searchAllAssets(ctx, false, &pair.IA, &pair.Ingress, nil, bwInKbps, startsAt, stopsAt, false) if err != nil { return nil, err } - ingressSearchAssets = filter(ingressAssetsResponse, func(a *hummingbird.SearchAsset) bool { + ingressSearchAssets = filter(ingressAssetsResponse, func(a *UnwrappedAsset) bool { return a.IfIdEgress == nil }) if len(ingressSearchAssets) != 0 { @@ -475,7 +503,7 @@ func (c *Client) checkoutAssetForInterfacePair( if err != nil { return nil, err } - egressSearchAssets = filter(egressAssetsResponse, func(a *hummingbird.SearchAsset) bool { + egressSearchAssets = filter(egressAssetsResponse, func(a *UnwrappedAsset) bool { return a.IfIdIngress == nil }) } @@ -484,8 +512,8 @@ func (c *Client) checkoutAssetForInterfacePair( return nil, err } - duration := stopsAt.Sub(startsAt) - actualPrice := func(a *hummingbird.SearchAsset) uint64 { + duration := time.Duration(stopsAt-startsAt) * time.Second + actualPrice := func(a *UnwrappedAsset) uint64 { price, err := ReservationPrice( a.Price, bwInKbps, @@ -498,30 +526,30 @@ func (c *Client) checkoutAssetForInterfacePair( } return price } - filterAssets := func(assets []*hummingbird.SearchAsset) []*hummingbird.SearchAsset { - return filter(assets, func(a *hummingbird.SearchAsset) bool { + filterAssets := func(assets []*UnwrappedAsset) []*UnwrappedAsset { + return filter(assets, func(a *UnwrappedAsset) bool { billableDuration, err := ReservationDuration(duration, a.TimeMinDuration, a.TimeGranularity) - if err != nil || a.StopsAt.AsTime().Before(startsAt.Add(billableDuration)) { + if err != nil || a.StopsAt < (startsAt+int64(billableDuration/time.Second)) { return false } return a.Bandwidth >= max(bwInKbps, a.BandwidthMin) }) } - sortAssets := func(assets []*hummingbird.SearchAsset) []*hummingbird.SearchAsset { + sortAssets := func(assets []*UnwrappedAsset) []*UnwrappedAsset { sort.Slice(assets, func(i, j int) bool { return actualPrice(assets[i]) < actualPrice(assets[j]) }) return assets } - buildBuyRequest := func(a *hummingbird.SearchAsset) *hummingbird.BuyAsset { + buildBuyRequest := func(a *UnwrappedAsset) *hummingbird.BuyAsset { billableDuration, _ := ReservationDuration(duration, a.TimeMinDuration, a.TimeGranularity) return &hummingbird.BuyAsset{ AssetId: a.AssetId, BandwidthExact: max(bwInKbps, a.BandwidthMin), - StartsAtExactly: timestamppb.New(startsAt), - StopsAtExactly: timestamppb.New(startsAt.Add(billableDuration)), + StartsAtExactly: startsAt, + StopsAtExactly: startsAt + int64(billableDuration/time.Second), } } ingressAssets := sortAssets(filterAssets(ingressSearchAssets)) @@ -556,8 +584,8 @@ func (c *Client) checkoutAssetForInterfacePair( type assetInfo struct { id []byte - startsAt time.Time - stopsAt time.Time + startsAt int64 + stopsAt int64 } func (c *Client) ObtainReservationsForInterfacePairs( @@ -576,12 +604,14 @@ func (c *Client) ObtainReservationsForInterfacePairs( if err != nil { return nil, err } + startsAtUnix := startsAt.Unix() + stopsAtUnix := stopsAt.Unix() combineCost := uint64(infoRep.Msg.SplitCombineFeeAbsolute) startsAt = startsAt.Truncate(time.Second) stopsAt = stopsAt.Truncate(time.Second) hops := make([]*snetpath.Hop, len(pairs)) if fetchReservations { - hops, err = c.findExistingReservations(ctx, pairs, bwInKbps, startsAt, stopsAt) + hops, err = c.findExistingReservations(ctx, pairs, bwInKbps, startsAtUnix, stopsAtUnix) if err != nil { return nil, err } @@ -598,7 +628,7 @@ func (c *Client) ObtainReservationsForInterfacePairs( } if combineAssets { o, err := c.checkoutAssetForInterfacePairWithCombine( - ctx, pair, bwInKbps, startsAt, stopsAt, combineCost) + ctx, pair, bwInKbps, startsAtUnix, stopsAtUnix, combineCost) if err != nil { if buyMode == FailOnError { return nil, err @@ -608,7 +638,7 @@ func (c *Client) ObtainReservationsForInterfacePairs( } orders = append(orders, o...) } else { - o, err := c.checkoutAssetForInterfacePair(ctx, pair, bwInKbps, startsAt, stopsAt) + o, err := c.checkoutAssetForInterfacePair(ctx, pair, bwInKbps, startsAtUnix, stopsAtUnix) if err != nil { if buyMode == FailOnError { return nil, err @@ -645,12 +675,12 @@ func (c *Client) ObtainReservationsForInterfacePairs( // Look up the assets that were just bought, to learn where and when they apply. // If combineAssets == True, we need to find assets that "touch" the time window. // If not, we need to find assets which each one of them covers the time window. - foundAssets, err := c.searchAllAssets(ctx, true, nil, nil, nil, bwInKbps, startsAt, stopsAt, combineAssets) + foundAssets, err := c.searchAllAssets(ctx, true, nil, nil, nil, bwInKbps, startsAtUnix, stopsAtUnix, combineAssets) if err != nil { return nil, err } - foundAssetsMap := make(map[uint64][]*hummingbird.SearchAsset) + foundAssetsMap := make(map[uint64][]*UnwrappedAsset) for _, boughtAsset := range boughtAssets { for _, foundAsset := range foundAssets { if bytes.Equal(boughtAsset.AssetId, foundAsset.AssetId) { @@ -658,7 +688,7 @@ func (c *Client) ObtainReservationsForInterfacePairs( if found { foundAssetsMap[foundAsset.Ia] = append(currentSlice, foundAsset) } else { - foundAssetsMap[foundAsset.Ia] = []*hummingbird.SearchAsset{foundAsset} + foundAssetsMap[foundAsset.Ia] = []*UnwrappedAsset{foundAsset} } break } @@ -666,9 +696,9 @@ func (c *Client) ObtainReservationsForInterfacePairs( } if combineAssets { for k, v := range foundAssetsMap { - ingressAssets := []*hummingbird.SearchAsset{} - egressAssets := []*hummingbird.SearchAsset{} - pairAssets := []*hummingbird.SearchAsset{} + ingressAssets := []*UnwrappedAsset{} + egressAssets := []*UnwrappedAsset{} + pairAssets := []*UnwrappedAsset{} for _, asset := range v { if asset.IfIdIngress != nil { if asset.IfIdEgress != nil { @@ -682,7 +712,7 @@ func (c *Client) ObtainReservationsForInterfacePairs( } if len(pairAssets) != 0 { sort.Slice(pairAssets, func(i, j int) bool { - return pairAssets[i].StartsAt.Seconds < pairAssets[j].StartsAt.Seconds + return pairAssets[i].StartsAt < pairAssets[j].StartsAt }) currAssetId := pairAssets[0].AssetId for i := 0; i < len(pairAssets)-1; i++ { @@ -702,12 +732,12 @@ func (c *Client) ObtainReservationsForInterfacePairs( } iaAssets[k] = []assetInfo{{ id: currAssetId, - startsAt: pairAssets[0].StartsAt.AsTime(), - stopsAt: pairAssets[len(pairAssets)-1].StopsAt.AsTime()}} + startsAt: pairAssets[0].StartsAt, + stopsAt: pairAssets[len(pairAssets)-1].StopsAt}} } if len(ingressAssets) != 0 && len(ingressAssets) == len(egressAssets) { sort.Slice(ingressAssets, func(i, j int) bool { - return ingressAssets[i].StartsAt.Seconds < ingressAssets[j].StartsAt.Seconds + return ingressAssets[i].StartsAt < ingressAssets[j].StartsAt }) currIngressAssetId := ingressAssets[0].AssetId for i := 0; i < len(ingressAssets)-1; i++ { @@ -725,7 +755,7 @@ func (c *Client) ObtainReservationsForInterfacePairs( currIngressAssetId = combineResp.Msg.AssetId } sort.Slice(egressAssets, func(i, j int) bool { - return egressAssets[i].StartsAt.Seconds < egressAssets[j].StartsAt.Seconds + return egressAssets[i].StartsAt < egressAssets[j].StartsAt }) currEgressAssetId := egressAssets[0].AssetId for i := 0; i < len(egressAssets)-1; i++ { @@ -745,13 +775,13 @@ func (c *Client) ObtainReservationsForInterfacePairs( iaAssets[k] = []assetInfo{ { id: currIngressAssetId, - startsAt: ingressAssets[0].StartsAt.AsTime(), - stopsAt: ingressAssets[len(ingressAssets)-1].StopsAt.AsTime(), + startsAt: ingressAssets[0].StartsAt, + stopsAt: ingressAssets[len(ingressAssets)-1].StopsAt, }, { id: currEgressAssetId, - startsAt: egressAssets[0].StartsAt.AsTime(), - stopsAt: egressAssets[len(egressAssets)-1].StopsAt.AsTime(), + startsAt: egressAssets[0].StartsAt, + stopsAt: egressAssets[len(egressAssets)-1].StopsAt, }, } } @@ -763,18 +793,18 @@ func (c *Client) ObtainReservationsForInterfacePairs( if found { iaAssets[asset.Ia] = append(current, assetInfo{ id: asset.AssetId, - stopsAt: asset.StopsAt.AsTime(), - startsAt: asset.StartsAt.AsTime()}) + stopsAt: asset.StopsAt, + startsAt: asset.StartsAt}) } else { iaAssets[asset.Ia] = []assetInfo{{ id: asset.AssetId, - stopsAt: asset.StopsAt.AsTime(), - startsAt: asset.StartsAt.AsTime()}} + stopsAt: asset.StopsAt, + startsAt: asset.StartsAt}} } } } } - redeemedHops, err := c.redeemHopsConcurrently(ctx, pairs, iaAssets, startsAt, stopsAt) + redeemedHops, err := c.redeemHopsConcurrently(ctx, pairs, iaAssets, startsAtUnix, stopsAtUnix) if err != nil { return nil, err } @@ -803,13 +833,13 @@ func (c *Client) redeemHopsConcurrently( ctx context.Context, pairs []InterfacePair, iaAssets map[uint64][]assetInfo, - startsAt time.Time, - stopsAt time.Time, + startsAt int64, + stopsAt int64, ) ([]*snetpath.Hop, error) { type requestInfo struct { request *hummingbird.RedeemAssetRequest - startsAt time.Time - stopsAt time.Time + startsAt int64 + stopsAt int64 } requests := make([]*requestInfo, len(pairs)) responses := make([]*hummingbird.RedeemAssetResponse, len(pairs)) @@ -839,8 +869,8 @@ func (c *Client) redeemHopsConcurrently( }, }, }, - startsAt: later(assets[0].startsAt, assets[1].startsAt), - stopsAt: earlier(assets[0].stopsAt, assets[1].stopsAt), + startsAt: max(assets[0].startsAt, assets[1].startsAt), + stopsAt: min(assets[0].stopsAt, assets[1].stopsAt), } } } @@ -875,8 +905,8 @@ func (c *Client) redeemHopsConcurrently( ResID: responses[i].ReservationId, Ak: [16]byte(responses[i].AuthenticationKey), Bw: uint16(responses[i].BwDataplaneEncoding), - StartTime: uint32(requests[i].startsAt.Unix()), - Duration: uint16(requests[i].stopsAt.Sub(requests[i].startsAt) / time.Second), + StartTime: uint32(requests[i].startsAt), + Duration: uint16(requests[i].stopsAt - requests[i].startsAt), }, } } else if errors[i] != nil { diff --git a/pkg/hummingbird/marketplace/client_test.go b/pkg/hummingbird/marketplace/client_test.go index 5b3fd7d540..fe188ca383 100644 --- a/pkg/hummingbird/marketplace/client_test.go +++ b/pkg/hummingbird/marketplace/client_test.go @@ -22,7 +22,6 @@ import ( "github.com/scionproto/scion/pkg/proto/hummingbird" "github.com/scionproto/scion/pkg/snet" "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/types/known/timestamppb" ) func TestInterfacePairsFromInterfaces(t *testing.T) { @@ -64,37 +63,37 @@ func TestIsSCIONURL(t *testing.T) { func TestCombine(t *testing.T) { type Test struct { - duration time.Duration + duration int64 bw uint32 splitCombineCost uint64 - assets []*hummingbird.SearchAsset + assets []*UnwrappedAsset expectedResponse []*hummingbird.BuyAsset } - start, _ := time.Parse(time.RFC3339, "2026-07-07T00:00:00Z") + start := time.Date(2026, 07, 07, 0, 0, 0, 0, time.UTC).Unix() tests := map[string]Test{ "baseCombine": { - duration: time.Second * 2, + duration: 2, bw: 1, expectedResponse: []*hummingbird.BuyAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - StartsAtExactly: timestamppb.New(start), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 1)), + StartsAtExactly: start, + StopsAtExactly: start + 1, BandwidthExact: 1, }, { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 2}, - StartsAtExactly: timestamppb.New(start.Add(time.Second * 1)), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 2)), + StartsAtExactly: start + 1, + StopsAtExactly: start + 2, BandwidthExact: 1, }, }, - assets: []*hummingbird.SearchAsset{ + assets: []*UnwrappedAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, Bandwidth: 1, - StartsAt: timestamppb.New(start), - StopsAt: timestamppb.New(start.Add(time.Second * 1)), + StartsAt: start, + StopsAt: start + 1, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -103,8 +102,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 2}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 1)), - StopsAt: timestamppb.New(start.Add(time.Second * 2)), + StartsAt: start + 1, + StopsAt: start + 2, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -113,28 +112,28 @@ func TestCombine(t *testing.T) { }, }, "baseCombineChooseCheaper": { - duration: time.Second * 2, + duration: 2, bw: 1, expectedResponse: []*hummingbird.BuyAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - StartsAtExactly: timestamppb.New(start), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 1)), + StartsAtExactly: start, + StopsAtExactly: start + 1, BandwidthExact: 1, }, { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 3}, - StartsAtExactly: timestamppb.New(start.Add(time.Second * 1)), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 2)), + StartsAtExactly: start + 1, + StopsAtExactly: start + 2, BandwidthExact: 1, }, }, - assets: []*hummingbird.SearchAsset{ + assets: []*UnwrappedAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, Bandwidth: 1, - StartsAt: timestamppb.New(start), - StopsAt: timestamppb.New(start.Add(time.Second * 1)), + StartsAt: start, + StopsAt: start + 1, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -143,8 +142,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 2}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 1)), - StopsAt: timestamppb.New(start.Add(time.Second * 2)), + StartsAt: start + 1, + StopsAt: start + 2, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -153,8 +152,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 3}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 1)), - StopsAt: timestamppb.New(start.Add(time.Second * 2)), + StartsAt: start + 1, + StopsAt: start + 2, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -163,40 +162,40 @@ func TestCombine(t *testing.T) { }, }, "combineOverlappingSolutions": { - duration: time.Second * 5, + duration: 5, bw: 1, expectedResponse: []*hummingbird.BuyAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - StartsAtExactly: timestamppb.New(start), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 1)), + StartsAtExactly: start, + StopsAtExactly: start + 1, BandwidthExact: 1, }, { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 2}, - StartsAtExactly: timestamppb.New(start.Add(time.Second * 1)), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 2)), + StartsAtExactly: start + 1, + StopsAtExactly: start + 2, BandwidthExact: 1, }, { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 3}, - StartsAtExactly: timestamppb.New(start.Add(time.Second * 2)), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 4)), + StartsAtExactly: start + 2, + StopsAtExactly: start + 4, BandwidthExact: 1, }, { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 4}, - StartsAtExactly: timestamppb.New(start.Add(time.Second * 4)), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 5)), + StartsAtExactly: start + 4, + StopsAtExactly: start + 5, BandwidthExact: 1, }, }, - assets: []*hummingbird.SearchAsset{ + assets: []*UnwrappedAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, Bandwidth: 1, - StartsAt: timestamppb.New(start), - StopsAt: timestamppb.New(start.Add(time.Second * 1)), + StartsAt: start, + StopsAt: start + 1, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -205,8 +204,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 2}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 1)), - StopsAt: timestamppb.New(start.Add(time.Second * 2)), + StartsAt: start + 1, + StopsAt: start + 2, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -215,8 +214,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 3}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 1)), - StopsAt: timestamppb.New(start.Add(time.Second * 4)), + StartsAt: start + 1, + StopsAt: start + 4, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -225,8 +224,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 4}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 4)), - StopsAt: timestamppb.New(start.Add(time.Second * 5)), + StartsAt: start + 4, + StopsAt: start + 5, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -235,35 +234,35 @@ func TestCombine(t *testing.T) { }, }, "combineOverlappingSolutionsWithCombineCost": { - duration: time.Second * 5, + duration: 5, bw: 1, splitCombineCost: 10, expectedResponse: []*hummingbird.BuyAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - StartsAtExactly: timestamppb.New(start), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 1)), + StartsAtExactly: start, + StopsAtExactly: start + 1, BandwidthExact: 1, }, { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 3}, - StartsAtExactly: timestamppb.New(start.Add(time.Second * 1)), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 4)), + StartsAtExactly: start + 1, + StopsAtExactly: start + 4, BandwidthExact: 1, }, { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 4}, - StartsAtExactly: timestamppb.New(start.Add(time.Second * 4)), - StopsAtExactly: timestamppb.New(start.Add(time.Second * 5)), + StartsAtExactly: start + 4, + StopsAtExactly: start + 5, BandwidthExact: 1, }, }, - assets: []*hummingbird.SearchAsset{ + assets: []*UnwrappedAsset{ { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, Bandwidth: 1, - StartsAt: timestamppb.New(start), - StopsAt: timestamppb.New(start.Add(time.Second * 1)), + StartsAt: start, + StopsAt: start + 1, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -272,8 +271,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 2}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 1)), - StopsAt: timestamppb.New(start.Add(time.Second * 2)), + StartsAt: start + 1, + StopsAt: start + 2, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -282,8 +281,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 3}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 1)), - StopsAt: timestamppb.New(start.Add(time.Second * 4)), + StartsAt: start + 1, + StopsAt: start + 4, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -292,8 +291,8 @@ func TestCombine(t *testing.T) { { AssetId: []byte{0, 0, 0, 0, 0, 0, 0, 4}, Bandwidth: 1, - StartsAt: timestamppb.New(start.Add(time.Second * 4)), - StopsAt: timestamppb.New(start.Add(time.Second * 5)), + StartsAt: start + 4, + StopsAt: start + 5, TimeGranularity: 1, BandwidthMin: 1, TimeMinDuration: 1, @@ -306,7 +305,7 @@ func TestCombine(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { assert.NotEqual(t, 0, tc.bw) - buyAssets, _, ok := c.recursiveSelectStart(tc.assets, tc.bw, start, start.Add(tc.duration), tc.splitCombineCost) + buyAssets, _, ok := c.recursiveSelectStart(tc.assets, tc.bw, start, start+tc.duration, tc.splitCombineCost) if tc.expectedResponse == nil { assert.False(t, ok) } else { @@ -314,8 +313,8 @@ func TestCombine(t *testing.T) { assert.Len(t, buyAssets, len(tc.expectedResponse)) for i := range buyAssets { assert.Equal(t, tc.expectedResponse[i].AssetId, buyAssets[i].AssetId) - assert.Equal(t, tc.expectedResponse[i].StartsAtExactly.Seconds, buyAssets[i].StartsAtExactly.Seconds) - assert.Equal(t, tc.expectedResponse[i].StopsAtExactly.Seconds, buyAssets[i].StopsAtExactly.Seconds) + assert.Equal(t, tc.expectedResponse[i].StartsAtExactly, buyAssets[i].StartsAtExactly) + assert.Equal(t, tc.expectedResponse[i].StopsAtExactly, buyAssets[i].StopsAtExactly) assert.Equal(t, tc.expectedResponse[i].BandwidthExact, buyAssets[i].BandwidthExact) } } diff --git a/pkg/proto/hummingbird/marketplace.pb.go b/pkg/proto/hummingbird/marketplace.pb.go index 393dbe9de7..b39f15545c 100644 --- a/pkg/proto/hummingbird/marketplace.pb.go +++ b/pkg/proto/hummingbird/marketplace.pb.go @@ -9,7 +9,6 @@ package hummingbird import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -414,8 +413,8 @@ type StatisticsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` IfIdIngress *uint32 `protobuf:"varint,1,opt,name=if_id_ingress,json=ifIdIngress,proto3,oneof" json:"if_id_ingress,omitempty"` IfIdEgress *uint32 `protobuf:"varint,2,opt,name=if_id_egress,json=ifIdEgress,proto3,oneof" json:"if_id_egress,omitempty"` - Start *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start,proto3" json:"start,omitempty"` - End *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=end,proto3" json:"end,omitempty"` + Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` + End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` Step uint32 `protobuf:"varint,5,opt,name=step,proto3" json:"step,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -465,18 +464,18 @@ func (x *StatisticsRequest) GetIfIdEgress() uint32 { return 0 } -func (x *StatisticsRequest) GetStart() *timestamppb.Timestamp { +func (x *StatisticsRequest) GetStart() int64 { if x != nil { return x.Start } - return nil + return 0 } -func (x *StatisticsRequest) GetEnd() *timestamppb.Timestamp { +func (x *StatisticsRequest) GetEnd() int64 { if x != nil { return x.End } - return nil + return 0 } func (x *StatisticsRequest) GetStep() uint32 { @@ -717,8 +716,8 @@ func (x *SplitBwOption) GetSplits() []uint32 { } type SplitTimeOption struct { - state protoimpl.MessageState `protogen:"open.v1"` - Splits []*timestamppb.Timestamp `protobuf:"bytes,1,rep,name=splits,proto3" json:"splits,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Splits []int64 `protobuf:"varint,1,rep,packed,name=splits,proto3" json:"splits,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -753,7 +752,7 @@ func (*SplitTimeOption) Descriptor() ([]byte, []int) { return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{10} } -func (x *SplitTimeOption) GetSplits() []*timestamppb.Timestamp { +func (x *SplitTimeOption) GetSplits() []int64 { if x != nil { return x.Splits } @@ -1155,8 +1154,8 @@ type PublisherAsset struct { Bandwidth uint32 `protobuf:"varint,3,opt,name=bandwidth,proto3" json:"bandwidth,omitempty"` BandwidthMin uint32 `protobuf:"varint,4,opt,name=bandwidth_min,json=bandwidthMin,proto3" json:"bandwidth_min,omitempty"` BandwidthMax uint32 `protobuf:"varint,5,opt,name=bandwidth_max,json=bandwidthMax,proto3" json:"bandwidth_max,omitempty"` - StartsAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` - StopsAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` + StartsAt int64 `protobuf:"varint,6,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` + StopsAt int64 `protobuf:"varint,7,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` TimeGranularity uint32 `protobuf:"varint,8,opt,name=time_granularity,json=timeGranularity,proto3" json:"time_granularity,omitempty"` TimeMinDuration uint32 `protobuf:"varint,9,opt,name=time_min_duration,json=timeMinDuration,proto3" json:"time_min_duration,omitempty"` TimeMaxDuration uint32 `protobuf:"varint,10,opt,name=time_max_duration,json=timeMaxDuration,proto3" json:"time_max_duration,omitempty"` @@ -1230,18 +1229,18 @@ func (x *PublisherAsset) GetBandwidthMax() uint32 { return 0 } -func (x *PublisherAsset) GetStartsAt() *timestamppb.Timestamp { +func (x *PublisherAsset) GetStartsAt() int64 { if x != nil { return x.StartsAt } - return nil + return 0 } -func (x *PublisherAsset) GetStopsAt() *timestamppb.Timestamp { +func (x *PublisherAsset) GetStopsAt() int64 { if x != nil { return x.StopsAt } - return nil + return 0 } func (x *PublisherAsset) GetTimeGranularity() uint32 { @@ -1596,15 +1595,13 @@ type SearchAsset struct { Ia uint64 `protobuf:"varint,2,opt,name=ia,proto3" json:"ia,omitempty"` IfIdIngress *uint32 `protobuf:"varint,3,opt,name=if_id_ingress,json=ifIdIngress,proto3,oneof" json:"if_id_ingress,omitempty"` IfIdEgress *uint32 `protobuf:"varint,4,opt,name=if_id_egress,json=ifIdEgress,proto3,oneof" json:"if_id_egress,omitempty"` - Bandwidth uint32 `protobuf:"varint,5,opt,name=bandwidth,proto3" json:"bandwidth,omitempty"` - BandwidthMin uint32 `protobuf:"varint,6,opt,name=bandwidth_min,json=bandwidthMin,proto3" json:"bandwidth_min,omitempty"` - BandwidthMax uint32 `protobuf:"varint,7,opt,name=bandwidth_max,json=bandwidthMax,proto3" json:"bandwidth_max,omitempty"` - StartsAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` - StopsAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` - TimeGranularity uint32 `protobuf:"varint,10,opt,name=time_granularity,json=timeGranularity,proto3" json:"time_granularity,omitempty"` - TimeMinDuration uint32 `protobuf:"varint,11,opt,name=time_min_duration,json=timeMinDuration,proto3" json:"time_min_duration,omitempty"` - TimeMaxDuration uint32 `protobuf:"varint,12,opt,name=time_max_duration,json=timeMaxDuration,proto3" json:"time_max_duration,omitempty"` - Price uint32 `protobuf:"varint,13,opt,name=price,proto3" json:"price,omitempty"` + BandwidthMin uint32 `protobuf:"varint,5,opt,name=bandwidth_min,json=bandwidthMin,proto3" json:"bandwidth_min,omitempty"` + BandwidthMax uint32 `protobuf:"varint,6,opt,name=bandwidth_max,json=bandwidthMax,proto3" json:"bandwidth_max,omitempty"` + TimeGranularity uint32 `protobuf:"varint,7,opt,name=time_granularity,json=timeGranularity,proto3" json:"time_granularity,omitempty"` + TimeMinDuration uint32 `protobuf:"varint,8,opt,name=time_min_duration,json=timeMinDuration,proto3" json:"time_min_duration,omitempty"` + TimeMaxDuration uint32 `protobuf:"varint,9,opt,name=time_max_duration,json=timeMaxDuration,proto3" json:"time_max_duration,omitempty"` + Price uint32 `protobuf:"varint,10,opt,name=price,proto3" json:"price,omitempty"` + Intervals []*SearchAssetInterval `protobuf:"bytes,11,rep,name=intervals,proto3" json:"intervals,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1667,65 +1664,111 @@ func (x *SearchAsset) GetIfIdEgress() uint32 { return 0 } -func (x *SearchAsset) GetBandwidth() uint32 { +func (x *SearchAsset) GetBandwidthMin() uint32 { if x != nil { - return x.Bandwidth + return x.BandwidthMin } return 0 } -func (x *SearchAsset) GetBandwidthMin() uint32 { +func (x *SearchAsset) GetBandwidthMax() uint32 { if x != nil { - return x.BandwidthMin + return x.BandwidthMax } return 0 } -func (x *SearchAsset) GetBandwidthMax() uint32 { +func (x *SearchAsset) GetTimeGranularity() uint32 { if x != nil { - return x.BandwidthMax + return x.TimeGranularity } return 0 } -func (x *SearchAsset) GetStartsAt() *timestamppb.Timestamp { +func (x *SearchAsset) GetTimeMinDuration() uint32 { if x != nil { - return x.StartsAt + return x.TimeMinDuration } - return nil + return 0 } -func (x *SearchAsset) GetStopsAt() *timestamppb.Timestamp { +func (x *SearchAsset) GetTimeMaxDuration() uint32 { if x != nil { - return x.StopsAt + return x.TimeMaxDuration } - return nil + return 0 } -func (x *SearchAsset) GetTimeGranularity() uint32 { +func (x *SearchAsset) GetPrice() uint32 { if x != nil { - return x.TimeGranularity + return x.Price } return 0 } -func (x *SearchAsset) GetTimeMinDuration() uint32 { +func (x *SearchAsset) GetIntervals() []*SearchAssetInterval { if x != nil { - return x.TimeMinDuration + return x.Intervals + } + return nil +} + +type SearchAssetInterval struct { + state protoimpl.MessageState `protogen:"open.v1"` + Bandwidth uint32 `protobuf:"varint,1,opt,name=bandwidth,proto3" json:"bandwidth,omitempty"` + StartsAt int64 `protobuf:"varint,2,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` + StopsAt int64 `protobuf:"varint,3,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SearchAssetInterval) Reset() { + *x = SearchAssetInterval{} + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchAssetInterval) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchAssetInterval) ProtoMessage() {} + +func (x *SearchAssetInterval) ProtoReflect() protoreflect.Message { + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchAssetInterval.ProtoReflect.Descriptor instead. +func (*SearchAssetInterval) Descriptor() ([]byte, []int) { + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{24} +} + +func (x *SearchAssetInterval) GetBandwidth() uint32 { + if x != nil { + return x.Bandwidth } return 0 } -func (x *SearchAsset) GetTimeMaxDuration() uint32 { +func (x *SearchAssetInterval) GetStartsAt() int64 { if x != nil { - return x.TimeMaxDuration + return x.StartsAt } return 0 } -func (x *SearchAsset) GetPrice() uint32 { +func (x *SearchAssetInterval) GetStopsAt() int64 { if x != nil { - return x.Price + return x.StopsAt } return 0 } @@ -1740,7 +1783,7 @@ type BuyAssetsRequest struct { func (x *BuyAssetsRequest) Reset() { *x = BuyAssetsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1752,7 +1795,7 @@ func (x *BuyAssetsRequest) String() string { func (*BuyAssetsRequest) ProtoMessage() {} func (x *BuyAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1765,7 +1808,7 @@ func (x *BuyAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAssetsRequest.ProtoReflect.Descriptor instead. func (*BuyAssetsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{24} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{25} } func (x *BuyAssetsRequest) GetAssets() []*BuyAsset { @@ -1785,8 +1828,8 @@ func (x *BuyAssetsRequest) GetMaxPrice() uint64 { type BuyAsset struct { state protoimpl.MessageState `protogen:"open.v1"` AssetId []byte `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` - StartsAtExactly *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=starts_at_exactly,json=startsAtExactly,proto3" json:"starts_at_exactly,omitempty"` - StopsAtExactly *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=stops_at_exactly,json=stopsAtExactly,proto3" json:"stops_at_exactly,omitempty"` + StartsAtExactly int64 `protobuf:"varint,2,opt,name=starts_at_exactly,json=startsAtExactly,proto3" json:"starts_at_exactly,omitempty"` + StopsAtExactly int64 `protobuf:"varint,3,opt,name=stops_at_exactly,json=stopsAtExactly,proto3" json:"stops_at_exactly,omitempty"` BandwidthExact uint32 `protobuf:"varint,4,opt,name=bandwidth_exact,json=bandwidthExact,proto3" json:"bandwidth_exact,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1794,7 +1837,7 @@ type BuyAsset struct { func (x *BuyAsset) Reset() { *x = BuyAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1806,7 +1849,7 @@ func (x *BuyAsset) String() string { func (*BuyAsset) ProtoMessage() {} func (x *BuyAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1819,7 +1862,7 @@ func (x *BuyAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAsset.ProtoReflect.Descriptor instead. func (*BuyAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{25} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{26} } func (x *BuyAsset) GetAssetId() []byte { @@ -1829,18 +1872,18 @@ func (x *BuyAsset) GetAssetId() []byte { return nil } -func (x *BuyAsset) GetStartsAtExactly() *timestamppb.Timestamp { +func (x *BuyAsset) GetStartsAtExactly() int64 { if x != nil { return x.StartsAtExactly } - return nil + return 0 } -func (x *BuyAsset) GetStopsAtExactly() *timestamppb.Timestamp { +func (x *BuyAsset) GetStopsAtExactly() int64 { if x != nil { return x.StopsAtExactly } - return nil + return 0 } func (x *BuyAsset) GetBandwidthExact() uint32 { @@ -1860,7 +1903,7 @@ type BuyAssetsResponse struct { func (x *BuyAssetsResponse) Reset() { *x = BuyAssetsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1872,7 +1915,7 @@ func (x *BuyAssetsResponse) String() string { func (*BuyAssetsResponse) ProtoMessage() {} func (x *BuyAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1885,7 +1928,7 @@ func (x *BuyAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAssetsResponse.ProtoReflect.Descriptor instead. func (*BuyAssetsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{26} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{27} } func (x *BuyAssetsResponse) GetAssets() []*BoughtAsset { @@ -1911,7 +1954,7 @@ type BoughtAsset struct { func (x *BoughtAsset) Reset() { *x = BoughtAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1923,7 +1966,7 @@ func (x *BoughtAsset) String() string { func (*BoughtAsset) ProtoMessage() {} func (x *BoughtAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1936,7 +1979,7 @@ func (x *BoughtAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use BoughtAsset.ProtoReflect.Descriptor instead. func (*BoughtAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{27} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{28} } func (x *BoughtAsset) GetAssetId() []byte { @@ -1959,7 +2002,7 @@ type RedeemAssetRequest struct { func (x *RedeemAssetRequest) Reset() { *x = RedeemAssetRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1971,7 +2014,7 @@ func (x *RedeemAssetRequest) String() string { func (*RedeemAssetRequest) ProtoMessage() {} func (x *RedeemAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1984,7 +2027,7 @@ func (x *RedeemAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemAssetRequest.ProtoReflect.Descriptor instead. func (*RedeemAssetRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{28} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{29} } func (x *RedeemAssetRequest) GetInterfaces() isRedeemAssetRequest_Interfaces { @@ -2038,7 +2081,7 @@ type IngressEgressPair struct { func (x *IngressEgressPair) Reset() { *x = IngressEgressPair{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2050,7 +2093,7 @@ func (x *IngressEgressPair) String() string { func (*IngressEgressPair) ProtoMessage() {} func (x *IngressEgressPair) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2063,7 +2106,7 @@ func (x *IngressEgressPair) ProtoReflect() protoreflect.Message { // Deprecated: Use IngressEgressPair.ProtoReflect.Descriptor instead. func (*IngressEgressPair) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{29} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{30} } func (x *IngressEgressPair) GetIngressAssetId() []byte { @@ -2092,7 +2135,7 @@ type RedeemAssetResponse struct { func (x *RedeemAssetResponse) Reset() { *x = RedeemAssetResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2104,7 +2147,7 @@ func (x *RedeemAssetResponse) String() string { func (*RedeemAssetResponse) ProtoMessage() {} func (x *RedeemAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2117,7 +2160,7 @@ func (x *RedeemAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemAssetResponse.ProtoReflect.Descriptor instead. func (*RedeemAssetResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{30} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{31} } func (x *RedeemAssetResponse) GetAuthenticationKey() []byte { @@ -2154,15 +2197,15 @@ type FetchReservationsRequest struct { IngressId *uint32 `protobuf:"varint,2,opt,name=ingress_id,json=ingressId,proto3,oneof" json:"ingress_id,omitempty"` EgressId *uint32 `protobuf:"varint,3,opt,name=egress_id,json=egressId,proto3,oneof" json:"egress_id,omitempty"` Bandwidth *uint32 `protobuf:"varint,4,opt,name=bandwidth,proto3,oneof" json:"bandwidth,omitempty"` - StartsAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=starts_at,json=startsAt,proto3,oneof" json:"starts_at,omitempty"` - StopsAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=stops_at,json=stopsAt,proto3,oneof" json:"stops_at,omitempty"` + StartsAt *int64 `protobuf:"varint,5,opt,name=starts_at,json=startsAt,proto3,oneof" json:"starts_at,omitempty"` + StopsAt *int64 `protobuf:"varint,6,opt,name=stops_at,json=stopsAt,proto3,oneof" json:"stops_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FetchReservationsRequest) Reset() { *x = FetchReservationsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2174,7 +2217,7 @@ func (x *FetchReservationsRequest) String() string { func (*FetchReservationsRequest) ProtoMessage() {} func (x *FetchReservationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2187,7 +2230,7 @@ func (x *FetchReservationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchReservationsRequest.ProtoReflect.Descriptor instead. func (*FetchReservationsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{31} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{32} } func (x *FetchReservationsRequest) GetIa() uint64 { @@ -2218,18 +2261,18 @@ func (x *FetchReservationsRequest) GetBandwidth() uint32 { return 0 } -func (x *FetchReservationsRequest) GetStartsAt() *timestamppb.Timestamp { - if x != nil { - return x.StartsAt +func (x *FetchReservationsRequest) GetStartsAt() int64 { + if x != nil && x.StartsAt != nil { + return *x.StartsAt } - return nil + return 0 } -func (x *FetchReservationsRequest) GetStopsAt() *timestamppb.Timestamp { - if x != nil { - return x.StopsAt +func (x *FetchReservationsRequest) GetStopsAt() int64 { + if x != nil && x.StopsAt != nil { + return *x.StopsAt } - return nil + return 0 } type FetchReservationsResponse struct { @@ -2241,7 +2284,7 @@ type FetchReservationsResponse struct { func (x *FetchReservationsResponse) Reset() { *x = FetchReservationsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2253,7 +2296,7 @@ func (x *FetchReservationsResponse) String() string { func (*FetchReservationsResponse) ProtoMessage() {} func (x *FetchReservationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2266,7 +2309,7 @@ func (x *FetchReservationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchReservationsResponse.ProtoReflect.Descriptor instead. func (*FetchReservationsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{32} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{33} } func (x *FetchReservationsResponse) GetReservations() []*Reservation { @@ -2284,8 +2327,8 @@ type Reservation struct { EgressId uint32 `protobuf:"varint,4,opt,name=egress_id,json=egressId,proto3" json:"egress_id,omitempty"` Bandwidth uint32 `protobuf:"varint,5,opt,name=bandwidth,proto3" json:"bandwidth,omitempty"` DataplaneEncoding uint32 `protobuf:"varint,6,opt,name=dataplane_encoding,json=dataplaneEncoding,proto3" json:"dataplane_encoding,omitempty"` - StartsAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` - StopsAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` + StartsAt int64 `protobuf:"varint,7,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` + StopsAt int64 `protobuf:"varint,8,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` AuthenticationKey []byte `protobuf:"bytes,9,opt,name=authentication_key,json=authenticationKey,proto3" json:"authentication_key,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -2293,7 +2336,7 @@ type Reservation struct { func (x *Reservation) Reset() { *x = Reservation{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2305,7 +2348,7 @@ func (x *Reservation) String() string { func (*Reservation) ProtoMessage() {} func (x *Reservation) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2318,7 +2361,7 @@ func (x *Reservation) ProtoReflect() protoreflect.Message { // Deprecated: Use Reservation.ProtoReflect.Descriptor instead. func (*Reservation) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{33} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{34} } func (x *Reservation) GetReservationId() uint32 { @@ -2363,18 +2406,18 @@ func (x *Reservation) GetDataplaneEncoding() uint32 { return 0 } -func (x *Reservation) GetStartsAt() *timestamppb.Timestamp { +func (x *Reservation) GetStartsAt() int64 { if x != nil { return x.StartsAt } - return nil + return 0 } -func (x *Reservation) GetStopsAt() *timestamppb.Timestamp { +func (x *Reservation) GetStopsAt() int64 { if x != nil { return x.StopsAt } - return nil + return 0 } func (x *Reservation) GetAuthenticationKey() []byte { @@ -2388,7 +2431,7 @@ var File_proto_hummingbird_v1_marketplace_proto protoreflect.FileDescriptor const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\n" + - "&proto/hummingbird/v1/marketplace.proto\x12\x14proto.hummingbird.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"P\n" + + "&proto/hummingbird/v1/marketplace.proto\x12\x14proto.hummingbird.v1\"P\n" + "\x13UpdateAssetsRequest\x129\n" + "\x06assets\x18\x01 \x03(\v2!.proto.hummingbird.v1.AssetUpdateR\x06assets\"\xad\x01\n" + "\vAssetUpdate\x12\x19\n" + @@ -2402,13 +2445,13 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\x11UpdateAssetResult\x12\x17\n" + "\x06new_id\x18\x01 \x01(\fH\x00R\x05newId\x12\x16\n" + "\x05error\x18\x02 \x01(\tH\x00R\x05errorB\r\n" + - "\vresult_type\"\xfa\x01\n" + + "\vresult_type\"\xc2\x01\n" + "\x11StatisticsRequest\x12'\n" + "\rif_id_ingress\x18\x01 \x01(\rH\x00R\vifIdIngress\x88\x01\x01\x12%\n" + "\fif_id_egress\x18\x02 \x01(\rH\x01R\n" + - "ifIdEgress\x88\x01\x01\x120\n" + - "\x05start\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x05start\x12,\n" + - "\x03end\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\x03end\x12\x12\n" + + "ifIdEgress\x88\x01\x01\x12\x14\n" + + "\x05start\x18\x03 \x01(\x03R\x05start\x12\x10\n" + + "\x03end\x18\x04 \x01(\x03R\x03end\x12\x12\n" + "\x04step\x18\x05 \x01(\rR\x04stepB\x10\n" + "\x0e_if_id_ingressB\x0f\n" + "\r_if_id_egress\"c\n" + @@ -2426,9 +2469,9 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "time_split\x18\x03 \x01(\v2%.proto.hummingbird.v1.SplitTimeOptionH\x00R\ttimeSplitB\x0e\n" + "\fsplit_option\"'\n" + "\rSplitBwOption\x12\x16\n" + - "\x06splits\x18\x01 \x03(\rR\x06splits\"E\n" + - "\x0fSplitTimeOption\x122\n" + - "\x06splits\x18\x01 \x03(\v2\x1a.google.protobuf.TimestampR\x06splits\"1\n" + + "\x06splits\x18\x01 \x03(\rR\x06splits\")\n" + + "\x0fSplitTimeOption\x12\x16\n" + + "\x06splits\x18\x01 \x03(\x03R\x06splits\"1\n" + "\x12SplitAssetResponse\x12\x1b\n" + "\tasset_ids\x18\x01 \x03(\fR\bassetIds\"2\n" + "\x13CombineAssetRequest\x12\x1b\n" + @@ -2453,16 +2496,16 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\x13PublishAssetRequest\x12:\n" + "\x05asset\x18\x01 \x01(\v2$.proto.hummingbird.v1.PublisherAssetR\x05asset\"1\n" + "\x14PublishAssetResponse\x12\x19\n" + - "\basset_id\x18\x01 \x01(\fR\aassetId\"\xf4\x03\n" + + "\basset_id\x18\x01 \x01(\fR\aassetId\"\xbc\x03\n" + "\x0ePublisherAsset\x12'\n" + "\rif_id_ingress\x18\x01 \x01(\rH\x00R\vifIdIngress\x88\x01\x01\x12%\n" + "\fif_id_egress\x18\x02 \x01(\rH\x01R\n" + "ifIdEgress\x88\x01\x01\x12\x1c\n" + "\tbandwidth\x18\x03 \x01(\rR\tbandwidth\x12#\n" + "\rbandwidth_min\x18\x04 \x01(\rR\fbandwidthMin\x12#\n" + - "\rbandwidth_max\x18\x05 \x01(\rR\fbandwidthMax\x127\n" + - "\tstarts_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\bstartsAt\x125\n" + - "\bstops_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\astopsAt\x12)\n" + + "\rbandwidth_max\x18\x05 \x01(\rR\fbandwidthMax\x12\x1b\n" + + "\tstarts_at\x18\x06 \x01(\x03R\bstartsAt\x12\x19\n" + + "\bstops_at\x18\a \x01(\x03R\astopsAt\x12)\n" + "\x10time_granularity\x18\b \x01(\rR\x0ftimeGranularity\x12*\n" + "\x11time_min_duration\x18\t \x01(\rR\x0ftimeMinDuration\x12*\n" + "\x11time_max_duration\x18\n" + @@ -2504,32 +2547,34 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\r_if_id_egressB\b\n" + "\x06_price\"Q\n" + "\x14SearchAssetsResponse\x129\n" + - "\x06assets\x18\x01 \x03(\v2!.proto.hummingbird.v1.SearchAssetR\x06assets\"\x9c\x04\n" + + "\x06assets\x18\x01 \x03(\v2!.proto.hummingbird.v1.SearchAssetR\x06assets\"\xd7\x03\n" + "\vSearchAsset\x12\x19\n" + "\basset_id\x18\x01 \x01(\fR\aassetId\x12\x0e\n" + "\x02ia\x18\x02 \x01(\x04R\x02ia\x12'\n" + "\rif_id_ingress\x18\x03 \x01(\rH\x00R\vifIdIngress\x88\x01\x01\x12%\n" + "\fif_id_egress\x18\x04 \x01(\rH\x01R\n" + - "ifIdEgress\x88\x01\x01\x12\x1c\n" + - "\tbandwidth\x18\x05 \x01(\rR\tbandwidth\x12#\n" + - "\rbandwidth_min\x18\x06 \x01(\rR\fbandwidthMin\x12#\n" + - "\rbandwidth_max\x18\a \x01(\rR\fbandwidthMax\x127\n" + - "\tstarts_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\bstartsAt\x125\n" + - "\bstops_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\astopsAt\x12)\n" + - "\x10time_granularity\x18\n" + - " \x01(\rR\x0ftimeGranularity\x12*\n" + - "\x11time_min_duration\x18\v \x01(\rR\x0ftimeMinDuration\x12*\n" + - "\x11time_max_duration\x18\f \x01(\rR\x0ftimeMaxDuration\x12\x14\n" + - "\x05price\x18\r \x01(\rR\x05priceB\x10\n" + + "ifIdEgress\x88\x01\x01\x12#\n" + + "\rbandwidth_min\x18\x05 \x01(\rR\fbandwidthMin\x12#\n" + + "\rbandwidth_max\x18\x06 \x01(\rR\fbandwidthMax\x12)\n" + + "\x10time_granularity\x18\a \x01(\rR\x0ftimeGranularity\x12*\n" + + "\x11time_min_duration\x18\b \x01(\rR\x0ftimeMinDuration\x12*\n" + + "\x11time_max_duration\x18\t \x01(\rR\x0ftimeMaxDuration\x12\x14\n" + + "\x05price\x18\n" + + " \x01(\rR\x05price\x12G\n" + + "\tintervals\x18\v \x03(\v2).proto.hummingbird.v1.SearchAssetIntervalR\tintervalsB\x10\n" + "\x0e_if_id_ingressB\x0f\n" + - "\r_if_id_egress\"g\n" + + "\r_if_id_egress\"k\n" + + "\x13SearchAssetInterval\x12\x1c\n" + + "\tbandwidth\x18\x01 \x01(\rR\tbandwidth\x12\x1b\n" + + "\tstarts_at\x18\x02 \x01(\x03R\bstartsAt\x12\x19\n" + + "\bstops_at\x18\x03 \x01(\x03R\astopsAt\"g\n" + "\x10BuyAssetsRequest\x126\n" + "\x06assets\x18\x01 \x03(\v2\x1e.proto.hummingbird.v1.BuyAssetR\x06assets\x12\x1b\n" + - "\tmax_price\x18\x02 \x01(\x04R\bmaxPrice\"\xdc\x01\n" + + "\tmax_price\x18\x02 \x01(\x04R\bmaxPrice\"\xa4\x01\n" + "\bBuyAsset\x12\x19\n" + - "\basset_id\x18\x01 \x01(\fR\aassetId\x12F\n" + - "\x11starts_at_exactly\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0fstartsAtExactly\x12D\n" + - "\x10stops_at_exactly\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x0estopsAtExactly\x12'\n" + + "\basset_id\x18\x01 \x01(\fR\aassetId\x12*\n" + + "\x11starts_at_exactly\x18\x02 \x01(\x03R\x0fstartsAtExactly\x12(\n" + + "\x10stops_at_exactly\x18\x03 \x01(\x03R\x0estopsAtExactly\x12'\n" + "\x0fbandwidth_exact\x18\x04 \x01(\rR\x0ebandwidthExact\"b\n" + "\x11BuyAssetsResponse\x129\n" + "\x06assets\x18\x01 \x03(\v2!.proto.hummingbird.v1.BoughtAssetR\x06assets\x12\x12\n" + @@ -2548,15 +2593,15 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\x12authentication_key\x18\x01 \x01(\fR\x11authenticationKey\x12%\n" + "\x0ereservation_id\x18\x02 \x01(\rR\rreservationId\x12+\n" + "\x11bandwidth_rounded\x18\x03 \x01(\rR\x10bandwidthRounded\x122\n" + - "\x15bw_dataplane_encoding\x18\x04 \x01(\rR\x13bwDataplaneEncoding\"\xdf\x02\n" + + "\x15bw_dataplane_encoding\x18\x04 \x01(\rR\x13bwDataplaneEncoding\"\xa7\x02\n" + "\x18FetchReservationsRequest\x12\x13\n" + "\x02ia\x18\x01 \x01(\x04H\x00R\x02ia\x88\x01\x01\x12\"\n" + "\n" + "ingress_id\x18\x02 \x01(\rH\x01R\tingressId\x88\x01\x01\x12 \n" + "\tegress_id\x18\x03 \x01(\rH\x02R\begressId\x88\x01\x01\x12!\n" + - "\tbandwidth\x18\x04 \x01(\rH\x03R\tbandwidth\x88\x01\x01\x12<\n" + - "\tstarts_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampH\x04R\bstartsAt\x88\x01\x01\x12:\n" + - "\bstops_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampH\x05R\astopsAt\x88\x01\x01B\x05\n" + + "\tbandwidth\x18\x04 \x01(\rH\x03R\tbandwidth\x88\x01\x01\x12 \n" + + "\tstarts_at\x18\x05 \x01(\x03H\x04R\bstartsAt\x88\x01\x01\x12\x1e\n" + + "\bstops_at\x18\x06 \x01(\x03H\x05R\astopsAt\x88\x01\x01B\x05\n" + "\x03_iaB\r\n" + "\v_ingress_idB\f\n" + "\n" + @@ -2567,7 +2612,7 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "_starts_atB\v\n" + "\t_stops_at\"b\n" + "\x19FetchReservationsResponse\x12E\n" + - "\freservations\x18\x01 \x03(\v2!.proto.hummingbird.v1.ReservationR\freservations\"\xec\x02\n" + + "\freservations\x18\x01 \x03(\v2!.proto.hummingbird.v1.ReservationR\freservations\"\xb4\x02\n" + "\vReservation\x12%\n" + "\x0ereservation_id\x18\x01 \x01(\rR\rreservationId\x12\x0e\n" + "\x02ia\x18\x02 \x01(\x04R\x02ia\x12\x1d\n" + @@ -2575,9 +2620,9 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "ingress_id\x18\x03 \x01(\rR\tingressId\x12\x1b\n" + "\tegress_id\x18\x04 \x01(\rR\begressId\x12\x1c\n" + "\tbandwidth\x18\x05 \x01(\rR\tbandwidth\x12-\n" + - "\x12dataplane_encoding\x18\x06 \x01(\rR\x11dataplaneEncoding\x127\n" + - "\tstarts_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\bstartsAt\x125\n" + - "\bstops_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\astopsAt\x12-\n" + + "\x12dataplane_encoding\x18\x06 \x01(\rR\x11dataplaneEncoding\x12\x1b\n" + + "\tstarts_at\x18\a \x01(\x03R\bstartsAt\x12\x19\n" + + "\bstops_at\x18\b \x01(\x03R\astopsAt\x12-\n" + "\x12authentication_key\x18\t \x01(\fR\x11authenticationKey*:\n" + "\x0fPricingStrategy\x12\x12\n" + "\x0estatic_pricing\x10\x00\x12\x13\n" + @@ -2612,7 +2657,7 @@ func file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP() []byte { } var file_proto_hummingbird_v1_marketplace_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_proto_hummingbird_v1_marketplace_proto_msgTypes = make([]protoimpl.MessageInfo, 34) +var file_proto_hummingbird_v1_marketplace_proto_msgTypes = make([]protoimpl.MessageInfo, 35) var file_proto_hummingbird_v1_marketplace_proto_goTypes = []any{ (PricingStrategy)(0), // 0: proto.hummingbird.v1.PricingStrategy (ValidityMode)(0), // 1: proto.hummingbird.v1.ValidityMode @@ -2640,74 +2685,62 @@ var file_proto_hummingbird_v1_marketplace_proto_goTypes = []any{ (*SearchListedAssetsRequest)(nil), // 23: proto.hummingbird.v1.SearchListedAssetsRequest (*SearchAssetsResponse)(nil), // 24: proto.hummingbird.v1.SearchAssetsResponse (*SearchAsset)(nil), // 25: proto.hummingbird.v1.SearchAsset - (*BuyAssetsRequest)(nil), // 26: proto.hummingbird.v1.BuyAssetsRequest - (*BuyAsset)(nil), // 27: proto.hummingbird.v1.BuyAsset - (*BuyAssetsResponse)(nil), // 28: proto.hummingbird.v1.BuyAssetsResponse - (*BoughtAsset)(nil), // 29: proto.hummingbird.v1.BoughtAsset - (*RedeemAssetRequest)(nil), // 30: proto.hummingbird.v1.RedeemAssetRequest - (*IngressEgressPair)(nil), // 31: proto.hummingbird.v1.IngressEgressPair - (*RedeemAssetResponse)(nil), // 32: proto.hummingbird.v1.RedeemAssetResponse - (*FetchReservationsRequest)(nil), // 33: proto.hummingbird.v1.FetchReservationsRequest - (*FetchReservationsResponse)(nil), // 34: proto.hummingbird.v1.FetchReservationsResponse - (*Reservation)(nil), // 35: proto.hummingbird.v1.Reservation - (*timestamppb.Timestamp)(nil), // 36: google.protobuf.Timestamp + (*SearchAssetInterval)(nil), // 26: proto.hummingbird.v1.SearchAssetInterval + (*BuyAssetsRequest)(nil), // 27: proto.hummingbird.v1.BuyAssetsRequest + (*BuyAsset)(nil), // 28: proto.hummingbird.v1.BuyAsset + (*BuyAssetsResponse)(nil), // 29: proto.hummingbird.v1.BuyAssetsResponse + (*BoughtAsset)(nil), // 30: proto.hummingbird.v1.BoughtAsset + (*RedeemAssetRequest)(nil), // 31: proto.hummingbird.v1.RedeemAssetRequest + (*IngressEgressPair)(nil), // 32: proto.hummingbird.v1.IngressEgressPair + (*RedeemAssetResponse)(nil), // 33: proto.hummingbird.v1.RedeemAssetResponse + (*FetchReservationsRequest)(nil), // 34: proto.hummingbird.v1.FetchReservationsRequest + (*FetchReservationsResponse)(nil), // 35: proto.hummingbird.v1.FetchReservationsResponse + (*Reservation)(nil), // 36: proto.hummingbird.v1.Reservation } var file_proto_hummingbird_v1_marketplace_proto_depIdxs = []int32{ 3, // 0: proto.hummingbird.v1.UpdateAssetsRequest.assets:type_name -> proto.hummingbird.v1.AssetUpdate 20, // 1: proto.hummingbird.v1.AssetUpdate.update:type_name -> proto.hummingbird.v1.PublisherAsset 4, // 2: proto.hummingbird.v1.AssetUpdate.remove:type_name -> proto.hummingbird.v1.Remove 6, // 3: proto.hummingbird.v1.UpdateAssetsResponse.result:type_name -> proto.hummingbird.v1.UpdateAssetResult - 36, // 4: proto.hummingbird.v1.StatisticsRequest.start:type_name -> google.protobuf.Timestamp - 36, // 5: proto.hummingbird.v1.StatisticsRequest.end:type_name -> google.protobuf.Timestamp - 9, // 6: proto.hummingbird.v1.StatisticsResponse.statistics:type_name -> proto.hummingbird.v1.StatisticsResponseEntry - 11, // 7: proto.hummingbird.v1.SplitAssetRequest.bw_split:type_name -> proto.hummingbird.v1.SplitBwOption - 12, // 8: proto.hummingbird.v1.SplitAssetRequest.time_split:type_name -> proto.hummingbird.v1.SplitTimeOption - 36, // 9: proto.hummingbird.v1.SplitTimeOption.splits:type_name -> google.protobuf.Timestamp - 0, // 10: proto.hummingbird.v1.MarketplaceInfoResponse.pricing_strategy:type_name -> proto.hummingbird.v1.PricingStrategy - 20, // 11: proto.hummingbird.v1.PublishAssetRequest.asset:type_name -> proto.hummingbird.v1.PublisherAsset - 36, // 12: proto.hummingbird.v1.PublisherAsset.starts_at:type_name -> google.protobuf.Timestamp - 36, // 13: proto.hummingbird.v1.PublisherAsset.stops_at:type_name -> google.protobuf.Timestamp - 22, // 14: proto.hummingbird.v1.SearchAssetsRequest.owned:type_name -> proto.hummingbird.v1.SearchOwnedAssetsRequest - 23, // 15: proto.hummingbird.v1.SearchAssetsRequest.listed:type_name -> proto.hummingbird.v1.SearchListedAssetsRequest - 1, // 16: proto.hummingbird.v1.SearchAssetsRequest.validity_mode:type_name -> proto.hummingbird.v1.ValidityMode - 25, // 17: proto.hummingbird.v1.SearchAssetsResponse.assets:type_name -> proto.hummingbird.v1.SearchAsset - 36, // 18: proto.hummingbird.v1.SearchAsset.starts_at:type_name -> google.protobuf.Timestamp - 36, // 19: proto.hummingbird.v1.SearchAsset.stops_at:type_name -> google.protobuf.Timestamp - 27, // 20: proto.hummingbird.v1.BuyAssetsRequest.assets:type_name -> proto.hummingbird.v1.BuyAsset - 36, // 21: proto.hummingbird.v1.BuyAsset.starts_at_exactly:type_name -> google.protobuf.Timestamp - 36, // 22: proto.hummingbird.v1.BuyAsset.stops_at_exactly:type_name -> google.protobuf.Timestamp - 29, // 23: proto.hummingbird.v1.BuyAssetsResponse.assets:type_name -> proto.hummingbird.v1.BoughtAsset - 31, // 24: proto.hummingbird.v1.RedeemAssetRequest.pair:type_name -> proto.hummingbird.v1.IngressEgressPair - 36, // 25: proto.hummingbird.v1.FetchReservationsRequest.starts_at:type_name -> google.protobuf.Timestamp - 36, // 26: proto.hummingbird.v1.FetchReservationsRequest.stops_at:type_name -> google.protobuf.Timestamp - 35, // 27: proto.hummingbird.v1.FetchReservationsResponse.reservations:type_name -> proto.hummingbird.v1.Reservation - 36, // 28: proto.hummingbird.v1.Reservation.starts_at:type_name -> google.protobuf.Timestamp - 36, // 29: proto.hummingbird.v1.Reservation.stops_at:type_name -> google.protobuf.Timestamp - 16, // 30: proto.hummingbird.v1.MarketplaceService.Info:input_type -> proto.hummingbird.v1.MarketplaceInfoRequest - 18, // 31: proto.hummingbird.v1.MarketplaceService.PublishAsset:input_type -> proto.hummingbird.v1.PublishAssetRequest - 21, // 32: proto.hummingbird.v1.MarketplaceService.SearchAssets:input_type -> proto.hummingbird.v1.SearchAssetsRequest - 26, // 33: proto.hummingbird.v1.MarketplaceService.BuyAssets:input_type -> proto.hummingbird.v1.BuyAssetsRequest - 30, // 34: proto.hummingbird.v1.MarketplaceService.RedeemAsset:input_type -> proto.hummingbird.v1.RedeemAssetRequest - 33, // 35: proto.hummingbird.v1.MarketplaceService.FetchReservations:input_type -> proto.hummingbird.v1.FetchReservationsRequest - 10, // 36: proto.hummingbird.v1.MarketplaceService.SplitAsset:input_type -> proto.hummingbird.v1.SplitAssetRequest - 14, // 37: proto.hummingbird.v1.MarketplaceService.CombineAssets:input_type -> proto.hummingbird.v1.CombineAssetRequest - 7, // 38: proto.hummingbird.v1.MarketplaceService.Statistics:input_type -> proto.hummingbird.v1.StatisticsRequest - 2, // 39: proto.hummingbird.v1.MarketplaceService.UpdateAssets:input_type -> proto.hummingbird.v1.UpdateAssetsRequest - 17, // 40: proto.hummingbird.v1.MarketplaceService.Info:output_type -> proto.hummingbird.v1.MarketplaceInfoResponse - 19, // 41: proto.hummingbird.v1.MarketplaceService.PublishAsset:output_type -> proto.hummingbird.v1.PublishAssetResponse - 24, // 42: proto.hummingbird.v1.MarketplaceService.SearchAssets:output_type -> proto.hummingbird.v1.SearchAssetsResponse - 28, // 43: proto.hummingbird.v1.MarketplaceService.BuyAssets:output_type -> proto.hummingbird.v1.BuyAssetsResponse - 32, // 44: proto.hummingbird.v1.MarketplaceService.RedeemAsset:output_type -> proto.hummingbird.v1.RedeemAssetResponse - 34, // 45: proto.hummingbird.v1.MarketplaceService.FetchReservations:output_type -> proto.hummingbird.v1.FetchReservationsResponse - 13, // 46: proto.hummingbird.v1.MarketplaceService.SplitAsset:output_type -> proto.hummingbird.v1.SplitAssetResponse - 15, // 47: proto.hummingbird.v1.MarketplaceService.CombineAssets:output_type -> proto.hummingbird.v1.CombineAssetResponse - 8, // 48: proto.hummingbird.v1.MarketplaceService.Statistics:output_type -> proto.hummingbird.v1.StatisticsResponse - 5, // 49: proto.hummingbird.v1.MarketplaceService.UpdateAssets:output_type -> proto.hummingbird.v1.UpdateAssetsResponse - 40, // [40:50] is the sub-list for method output_type - 30, // [30:40] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 9, // 4: proto.hummingbird.v1.StatisticsResponse.statistics:type_name -> proto.hummingbird.v1.StatisticsResponseEntry + 11, // 5: proto.hummingbird.v1.SplitAssetRequest.bw_split:type_name -> proto.hummingbird.v1.SplitBwOption + 12, // 6: proto.hummingbird.v1.SplitAssetRequest.time_split:type_name -> proto.hummingbird.v1.SplitTimeOption + 0, // 7: proto.hummingbird.v1.MarketplaceInfoResponse.pricing_strategy:type_name -> proto.hummingbird.v1.PricingStrategy + 20, // 8: proto.hummingbird.v1.PublishAssetRequest.asset:type_name -> proto.hummingbird.v1.PublisherAsset + 22, // 9: proto.hummingbird.v1.SearchAssetsRequest.owned:type_name -> proto.hummingbird.v1.SearchOwnedAssetsRequest + 23, // 10: proto.hummingbird.v1.SearchAssetsRequest.listed:type_name -> proto.hummingbird.v1.SearchListedAssetsRequest + 1, // 11: proto.hummingbird.v1.SearchAssetsRequest.validity_mode:type_name -> proto.hummingbird.v1.ValidityMode + 25, // 12: proto.hummingbird.v1.SearchAssetsResponse.assets:type_name -> proto.hummingbird.v1.SearchAsset + 26, // 13: proto.hummingbird.v1.SearchAsset.intervals:type_name -> proto.hummingbird.v1.SearchAssetInterval + 28, // 14: proto.hummingbird.v1.BuyAssetsRequest.assets:type_name -> proto.hummingbird.v1.BuyAsset + 30, // 15: proto.hummingbird.v1.BuyAssetsResponse.assets:type_name -> proto.hummingbird.v1.BoughtAsset + 32, // 16: proto.hummingbird.v1.RedeemAssetRequest.pair:type_name -> proto.hummingbird.v1.IngressEgressPair + 36, // 17: proto.hummingbird.v1.FetchReservationsResponse.reservations:type_name -> proto.hummingbird.v1.Reservation + 16, // 18: proto.hummingbird.v1.MarketplaceService.Info:input_type -> proto.hummingbird.v1.MarketplaceInfoRequest + 18, // 19: proto.hummingbird.v1.MarketplaceService.PublishAsset:input_type -> proto.hummingbird.v1.PublishAssetRequest + 21, // 20: proto.hummingbird.v1.MarketplaceService.SearchAssets:input_type -> proto.hummingbird.v1.SearchAssetsRequest + 27, // 21: proto.hummingbird.v1.MarketplaceService.BuyAssets:input_type -> proto.hummingbird.v1.BuyAssetsRequest + 31, // 22: proto.hummingbird.v1.MarketplaceService.RedeemAsset:input_type -> proto.hummingbird.v1.RedeemAssetRequest + 34, // 23: proto.hummingbird.v1.MarketplaceService.FetchReservations:input_type -> proto.hummingbird.v1.FetchReservationsRequest + 10, // 24: proto.hummingbird.v1.MarketplaceService.SplitAsset:input_type -> proto.hummingbird.v1.SplitAssetRequest + 14, // 25: proto.hummingbird.v1.MarketplaceService.CombineAssets:input_type -> proto.hummingbird.v1.CombineAssetRequest + 7, // 26: proto.hummingbird.v1.MarketplaceService.Statistics:input_type -> proto.hummingbird.v1.StatisticsRequest + 2, // 27: proto.hummingbird.v1.MarketplaceService.UpdateAssets:input_type -> proto.hummingbird.v1.UpdateAssetsRequest + 17, // 28: proto.hummingbird.v1.MarketplaceService.Info:output_type -> proto.hummingbird.v1.MarketplaceInfoResponse + 19, // 29: proto.hummingbird.v1.MarketplaceService.PublishAsset:output_type -> proto.hummingbird.v1.PublishAssetResponse + 24, // 30: proto.hummingbird.v1.MarketplaceService.SearchAssets:output_type -> proto.hummingbird.v1.SearchAssetsResponse + 29, // 31: proto.hummingbird.v1.MarketplaceService.BuyAssets:output_type -> proto.hummingbird.v1.BuyAssetsResponse + 33, // 32: proto.hummingbird.v1.MarketplaceService.RedeemAsset:output_type -> proto.hummingbird.v1.RedeemAssetResponse + 35, // 33: proto.hummingbird.v1.MarketplaceService.FetchReservations:output_type -> proto.hummingbird.v1.FetchReservationsResponse + 13, // 34: proto.hummingbird.v1.MarketplaceService.SplitAsset:output_type -> proto.hummingbird.v1.SplitAssetResponse + 15, // 35: proto.hummingbird.v1.MarketplaceService.CombineAssets:output_type -> proto.hummingbird.v1.CombineAssetResponse + 8, // 36: proto.hummingbird.v1.MarketplaceService.Statistics:output_type -> proto.hummingbird.v1.StatisticsResponse + 5, // 37: proto.hummingbird.v1.MarketplaceService.UpdateAssets:output_type -> proto.hummingbird.v1.UpdateAssetsResponse + 28, // [28:38] is the sub-list for method output_type + 18, // [18:28] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_proto_hummingbird_v1_marketplace_proto_init() } @@ -2736,18 +2769,18 @@ func file_proto_hummingbird_v1_marketplace_proto_init() { file_proto_hummingbird_v1_marketplace_proto_msgTypes[20].OneofWrappers = []any{} file_proto_hummingbird_v1_marketplace_proto_msgTypes[21].OneofWrappers = []any{} file_proto_hummingbird_v1_marketplace_proto_msgTypes[23].OneofWrappers = []any{} - file_proto_hummingbird_v1_marketplace_proto_msgTypes[28].OneofWrappers = []any{ + file_proto_hummingbird_v1_marketplace_proto_msgTypes[29].OneofWrappers = []any{ (*RedeemAssetRequest_Pair)(nil), (*RedeemAssetRequest_IfPairAssetId)(nil), } - file_proto_hummingbird_v1_marketplace_proto_msgTypes[31].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[32].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_hummingbird_v1_marketplace_proto_rawDesc), len(file_proto_hummingbird_v1_marketplace_proto_rawDesc)), NumEnums: 2, - NumMessages: 34, + NumMessages: 35, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/proto/hummingbird/redemption.pb.go b/pkg/proto/hummingbird/redemption.pb.go index 1179822c68..53de7aebcd 100644 --- a/pkg/proto/hummingbird/redemption.pb.go +++ b/pkg/proto/hummingbird/redemption.pb.go @@ -9,7 +9,6 @@ package hummingbird import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -24,7 +23,7 @@ const ( type DelegateRedemptionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` + ExpirationTime int64 `protobuf:"varint,1,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` ReservationIdLowerBound uint32 `protobuf:"varint,2,opt,name=reservation_id_lower_bound,json=reservationIdLowerBound,proto3" json:"reservation_id_lower_bound,omitempty"` ReservationIdUpperBound uint32 `protobuf:"varint,3,opt,name=reservation_id_upper_bound,json=reservationIdUpperBound,proto3" json:"reservation_id_upper_bound,omitempty"` Key []byte `protobuf:"bytes,4,opt,name=Key,proto3" json:"Key,omitempty"` @@ -63,11 +62,11 @@ func (*DelegateRedemptionRequest) Descriptor() ([]byte, []int) { return file_proto_hummingbird_v1_redemption_proto_rawDescGZIP(), []int{0} } -func (x *DelegateRedemptionRequest) GetExpirationTime() *timestamppb.Timestamp { +func (x *DelegateRedemptionRequest) GetExpirationTime() int64 { if x != nil { return x.ExpirationTime } - return nil + return 0 } func (x *DelegateRedemptionRequest) GetReservationIdLowerBound() uint32 { @@ -100,7 +99,7 @@ func (x *DelegateRedemptionRequest) GetEncodingPoints() []uint32 { type DelegateRedemptionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` + ExpirationTime int64 `protobuf:"varint,1,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -135,11 +134,11 @@ func (*DelegateRedemptionResponse) Descriptor() ([]byte, []int) { return file_proto_hummingbird_v1_redemption_proto_rawDescGZIP(), []int{1} } -func (x *DelegateRedemptionResponse) GetExpirationTime() *timestamppb.Timestamp { +func (x *DelegateRedemptionResponse) GetExpirationTime() int64 { if x != nil { return x.ExpirationTime } - return nil + return 0 } type RedeemAssetFromASRequest struct { @@ -147,8 +146,8 @@ type RedeemAssetFromASRequest struct { IngressId uint32 `protobuf:"varint,1,opt,name=ingress_id,json=ingressId,proto3" json:"ingress_id,omitempty"` EgressId uint32 `protobuf:"varint,2,opt,name=egress_id,json=egressId,proto3" json:"egress_id,omitempty"` Bandwidth uint32 `protobuf:"varint,3,opt,name=bandwidth,proto3" json:"bandwidth,omitempty"` - StartsAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` - StopsAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` + StartsAt int64 `protobuf:"varint,4,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` + StopsAt int64 `protobuf:"varint,5,opt,name=stops_at,json=stopsAt,proto3" json:"stops_at,omitempty"` RequestId uint64 `protobuf:"varint,6,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -205,18 +204,18 @@ func (x *RedeemAssetFromASRequest) GetBandwidth() uint32 { return 0 } -func (x *RedeemAssetFromASRequest) GetStartsAt() *timestamppb.Timestamp { +func (x *RedeemAssetFromASRequest) GetStartsAt() int64 { if x != nil { return x.StartsAt } - return nil + return 0 } -func (x *RedeemAssetFromASRequest) GetStopsAt() *timestamppb.Timestamp { +func (x *RedeemAssetFromASRequest) GetStopsAt() int64 { if x != nil { return x.StopsAt } - return nil + return 0 } func (x *RedeemAssetFromASRequest) GetRequestId() uint64 { @@ -388,22 +387,22 @@ var File_proto_hummingbird_v1_redemption_proto protoreflect.FileDescriptor const file_proto_hummingbird_v1_redemption_proto_rawDesc = "" + "\n" + - "%proto/hummingbird/v1/redemption.proto\x12\x14proto.hummingbird.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\x95\x02\n" + - "\x19DelegateRedemptionRequest\x12C\n" + - "\x0fexpiration_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\x12;\n" + + "%proto/hummingbird/v1/redemption.proto\x12\x14proto.hummingbird.v1\"\xf9\x01\n" + + "\x19DelegateRedemptionRequest\x12'\n" + + "\x0fexpiration_time\x18\x01 \x01(\x03R\x0eexpirationTime\x12;\n" + "\x1areservation_id_lower_bound\x18\x02 \x01(\rR\x17reservationIdLowerBound\x12;\n" + "\x1areservation_id_upper_bound\x18\x03 \x01(\rR\x17reservationIdUpperBound\x12\x10\n" + "\x03Key\x18\x04 \x01(\fR\x03Key\x12'\n" + - "\x0fencoding_points\x18\x05 \x03(\rR\x0eencodingPoints\"a\n" + - "\x1aDelegateRedemptionResponse\x12C\n" + - "\x0fexpiration_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\"\x83\x02\n" + + "\x0fencoding_points\x18\x05 \x03(\rR\x0eencodingPoints\"E\n" + + "\x1aDelegateRedemptionResponse\x12'\n" + + "\x0fexpiration_time\x18\x01 \x01(\x03R\x0eexpirationTime\"\xcb\x01\n" + "\x18RedeemAssetFromASRequest\x12\x1d\n" + "\n" + "ingress_id\x18\x01 \x01(\rR\tingressId\x12\x1b\n" + "\tegress_id\x18\x02 \x01(\rR\begressId\x12\x1c\n" + - "\tbandwidth\x18\x03 \x01(\rR\tbandwidth\x127\n" + - "\tstarts_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\bstartsAt\x125\n" + - "\bstops_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\astopsAt\x12\x1d\n" + + "\tbandwidth\x18\x03 \x01(\rR\tbandwidth\x12\x1b\n" + + "\tstarts_at\x18\x04 \x01(\x03R\bstartsAt\x12\x19\n" + + "\bstops_at\x18\x05 \x01(\x03R\astopsAt\x12\x1d\n" + "\n" + "request_id\x18\x06 \x01(\x04R\trequestId\"\xa0\x01\n" + "\x19RedeemAssetFromASResponse\x12\x1d\n" + @@ -440,23 +439,18 @@ var file_proto_hummingbird_v1_redemption_proto_goTypes = []any{ (*RedeemAssetFromASRequest)(nil), // 2: proto.hummingbird.v1.RedeemAssetFromASRequest (*RedeemAssetFromASResponse)(nil), // 3: proto.hummingbird.v1.RedeemAssetFromASResponse (*ReservationInfo)(nil), // 4: proto.hummingbird.v1.ReservationInfo - (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp } var file_proto_hummingbird_v1_redemption_proto_depIdxs = []int32{ - 5, // 0: proto.hummingbird.v1.DelegateRedemptionRequest.expiration_time:type_name -> google.protobuf.Timestamp - 5, // 1: proto.hummingbird.v1.DelegateRedemptionResponse.expiration_time:type_name -> google.protobuf.Timestamp - 5, // 2: proto.hummingbird.v1.RedeemAssetFromASRequest.starts_at:type_name -> google.protobuf.Timestamp - 5, // 3: proto.hummingbird.v1.RedeemAssetFromASRequest.stops_at:type_name -> google.protobuf.Timestamp - 4, // 4: proto.hummingbird.v1.RedeemAssetFromASResponse.res_info:type_name -> proto.hummingbird.v1.ReservationInfo - 3, // 5: proto.hummingbird.v1.RedemptionService.RedeemASAsset:input_type -> proto.hummingbird.v1.RedeemAssetFromASResponse - 0, // 6: proto.hummingbird.v1.RedemptionService.DelegateRedemption:input_type -> proto.hummingbird.v1.DelegateRedemptionRequest - 2, // 7: proto.hummingbird.v1.RedemptionService.RedeemASAsset:output_type -> proto.hummingbird.v1.RedeemAssetFromASRequest - 1, // 8: proto.hummingbird.v1.RedemptionService.DelegateRedemption:output_type -> proto.hummingbird.v1.DelegateRedemptionResponse - 7, // [7:9] is the sub-list for method output_type - 5, // [5:7] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 4, // 0: proto.hummingbird.v1.RedeemAssetFromASResponse.res_info:type_name -> proto.hummingbird.v1.ReservationInfo + 3, // 1: proto.hummingbird.v1.RedemptionService.RedeemASAsset:input_type -> proto.hummingbird.v1.RedeemAssetFromASResponse + 0, // 2: proto.hummingbird.v1.RedemptionService.DelegateRedemption:input_type -> proto.hummingbird.v1.DelegateRedemptionRequest + 2, // 3: proto.hummingbird.v1.RedemptionService.RedeemASAsset:output_type -> proto.hummingbird.v1.RedeemAssetFromASRequest + 1, // 4: proto.hummingbird.v1.RedemptionService.DelegateRedemption:output_type -> proto.hummingbird.v1.DelegateRedemptionResponse + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_proto_hummingbird_v1_redemption_proto_init() } diff --git a/proto/hummingbird/v1/marketplace.proto b/proto/hummingbird/v1/marketplace.proto index 1388f7c765..3227d8ba9f 100644 --- a/proto/hummingbird/v1/marketplace.proto +++ b/proto/hummingbird/v1/marketplace.proto @@ -18,8 +18,6 @@ option go_package = "github.com/scionproto/scion/pkg/proto/hummingbird"; package proto.hummingbird.v1; -import "google/protobuf/timestamp.proto"; - service MarketplaceService { rpc Info(MarketplaceInfoRequest) returns (MarketplaceInfoResponse) {} rpc PublishAsset(PublishAssetRequest) returns (PublishAssetResponse) {} @@ -57,8 +55,8 @@ message UpdateAssetResult { message StatisticsRequest { optional uint32 if_id_ingress = 1; optional uint32 if_id_egress = 2; - google.protobuf.Timestamp start = 3; - google.protobuf.Timestamp end = 4; + int64 start = 3; + int64 end = 4; uint32 step = 5; } @@ -83,7 +81,7 @@ message SplitBwOption { repeated uint32 splits = 1; } message SplitTimeOption { - repeated google.protobuf.Timestamp splits = 1; + repeated int64 splits = 1; } // Returns the asset IDs created from the split. message SplitAssetResponse { @@ -138,8 +136,8 @@ message PublisherAsset { uint32 bandwidth = 3; uint32 bandwidth_min = 4; uint32 bandwidth_max = 5; - google.protobuf.Timestamp starts_at = 6; - google.protobuf.Timestamp stops_at = 7; + int64 starts_at = 6; + int64 stops_at = 7; uint32 time_granularity = 8; uint32 time_min_duration = 9; uint32 time_max_duration = 10; @@ -187,15 +185,18 @@ message SearchAsset { uint64 ia = 2; optional uint32 if_id_ingress = 3; optional uint32 if_id_egress = 4; - uint32 bandwidth = 5; - uint32 bandwidth_min = 6; - uint32 bandwidth_max = 7; - google.protobuf.Timestamp starts_at = 8; - google.protobuf.Timestamp stops_at = 9; - uint32 time_granularity = 10; - uint32 time_min_duration = 11; - uint32 time_max_duration = 12; - uint32 price = 13; + uint32 bandwidth_min = 5; + uint32 bandwidth_max = 6; + uint32 time_granularity = 7; + uint32 time_min_duration = 8; + uint32 time_max_duration = 9; + uint32 price = 10; + repeated SearchAssetInterval intervals = 11; +} +message SearchAssetInterval { + uint32 bandwidth = 1; + int64 starts_at = 2; + int64 stops_at = 3; } message BuyAssetsRequest { @@ -205,8 +206,8 @@ message BuyAssetsRequest { message BuyAsset { bytes asset_id = 1; - google.protobuf.Timestamp starts_at_exactly = 2; - google.protobuf.Timestamp stops_at_exactly = 3; + int64 starts_at_exactly = 2; + int64 stops_at_exactly = 3; uint32 bandwidth_exact = 4; } @@ -242,8 +243,8 @@ message FetchReservationsRequest { optional uint32 ingress_id = 2; optional uint32 egress_id = 3; optional uint32 bandwidth = 4; - optional google.protobuf.Timestamp starts_at = 5; - optional google.protobuf.Timestamp stops_at = 6; + optional int64 starts_at = 5; + optional int64 stops_at = 6; } message FetchReservationsResponse { @@ -257,7 +258,7 @@ message Reservation { uint32 egress_id = 4; uint32 bandwidth = 5; uint32 dataplane_encoding = 6; - google.protobuf.Timestamp starts_at = 7; - google.protobuf.Timestamp stops_at = 8; + int64 starts_at = 7; + int64 stops_at = 8; bytes authentication_key = 9; } diff --git a/proto/hummingbird/v1/redemption.proto b/proto/hummingbird/v1/redemption.proto index fd306e6dfc..0daadad6ac 100644 --- a/proto/hummingbird/v1/redemption.proto +++ b/proto/hummingbird/v1/redemption.proto @@ -18,7 +18,6 @@ option go_package = "github.com/scionproto/scion/pkg/proto/hummingbird"; package proto.hummingbird.v1; -import "google/protobuf/timestamp.proto"; service RedemptionService { rpc RedeemASAsset(stream RedeemAssetFromASResponse) returns (stream RedeemAssetFromASRequest) {} @@ -27,7 +26,7 @@ service RedemptionService { message DelegateRedemptionRequest { // delegate request valid from now until expiration_time - google.protobuf.Timestamp expiration_time = 1; + int64 expiration_time = 1; // the start of the reservation ID interval uint32 reservation_id_lower_bound = 2; // the exclusive end of the reservation ID interval @@ -38,15 +37,15 @@ message DelegateRedemptionRequest { repeated uint32 encoding_points = 5; } message DelegateRedemptionResponse { - google.protobuf.Timestamp expiration_time = 1; + int64 expiration_time = 1; } message RedeemAssetFromASRequest { uint32 ingress_id = 1; uint32 egress_id = 2; uint32 bandwidth = 3; - google.protobuf.Timestamp starts_at = 4; - google.protobuf.Timestamp stops_at = 5; + int64 starts_at = 4; + int64 stops_at = 5; // to match request with response uint64 request_id = 6; } diff --git a/tools/topology/marketplace.py b/tools/topology/marketplace.py index 39a873f111..f6efaed2f1 100644 --- a/tools/topology/marketplace.py +++ b/tools/topology/marketplace.py @@ -660,8 +660,8 @@ def insertAssets(db, assets): a.get("ingress"), a.get("egress"), a.get("bandwidth"), - a.get("starts_at"), - a.get("stops_at"), + rfc3339_to_unix(a.get("starts_at")), + rfc3339_to_unix(a.get("stops_at")), a.get("price"), )) @@ -673,8 +673,8 @@ def insertAssets(db, assets): a.get("ingress"), a.get("egress"), a.get("bandwidth"), - a.get("starts_at"), - a.get("stops_at"), + rfc3339_to_unix(a.get("starts_at")), + rfc3339_to_unix(a.get("stops_at")), a.get("price"), )) @@ -708,7 +708,7 @@ def insertReservations(db, reservations): isd_id, as_id = iaNumbers(r.get("ia")) rows.append(( r.get("id"), isd_id, as_id, r.get("ingress"), r.get("egress"), r.get("bandwidth"), - r.get("bw_encoded"), r.get("starts_at"), r.get("stops_at"), + r.get("bw_encoded"), rfc3339_to_unix(r.get("starts_at")), rfc3339_to_unix(r.get("stops_at")), bytes.fromhex(r.get("key")), r.get("owner"), )) db.executemany( @@ -738,7 +738,8 @@ def insertDelegations(db, delegations): ) rows.append(( isd_id, as_id, d.get("res_id_limit_low"), d.get("res_id_limit_high"), - d.get("expiration"), d.get("paid_until"), bytes.fromhex(d.get("key")), encodings, + rfc3339_to_unix(d.get("expiration")), rfc3339_to_unix(d.get("paid_until")), + bytes.fromhex(d.get("key")), encodings, )) db.executemany( """ From 0ba77fab3b2c263a6aee251622356cf502af2dbd Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:21:17 +0200 Subject: [PATCH 07/14] reject redemption if ingress and egress assets are provided with mismatching bandwidth or validity period --- marketplace/service.go | 21 ++++++++++++++------- marketplace/storage/storage.go | 5 ++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/marketplace/service.go b/marketplace/service.go index 26ead16a17..ebdadc8188 100644 --- a/marketplace/service.go +++ b/marketplace/service.go @@ -447,15 +447,26 @@ func (s *Service) RedeemAsset( // ingress and egress assets ingressAsset := assets[0] egressAsset := assets[1] - bw = min(ingressAsset.Bandwidth, egressAsset.Bandwidth) + if ingressAsset.IA != egressAsset.IA || + ingressAsset.Bandwidth != egressAsset.Bandwidth || + ingressAsset.StartAt != egressAsset.StartAt || + ingressAsset.StopsAt != egressAsset.StopsAt || + !ingressAsset.IfIdIngress.Valid || + !egressAsset.IfIdEgress.Valid { + return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("invalid assets")) + } + bw = ingressAsset.Bandwidth ingressID = uint32(ingressAsset.IfIdIngress.Int32) egressID = uint32(egressAsset.IfIdEgress.Int32) ia = ingressAsset.IA - startsAt = max(ingressAsset.StartAt, egressAsset.StartAt) - stopsAt = min(ingressAsset.StopsAt, egressAsset.StopsAt) + startsAt = ingressAsset.StartAt + stopsAt = ingressAsset.StopsAt } else { return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("invalid assets")) } + if stopsAt <= startsAt { + return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("invalid validity range")) + } undoRedemption := func() error { // we use context.Background here because if the client disconnected in the meantime, // we cannot undo the redemption using the request's context. @@ -473,10 +484,6 @@ func (s *Service) RedeemAsset( } return err } - if stopsAt <= startsAt { - return nil, connect.NewError(connect.CodeInvalidArgument, - serrors.Join(serrors.New("invalid validity range"), undoRedemption())) - } peer := s.FindRedemptionServerHandler(ia) // The deadline is derived from the request context: a client who disconnects releases // the asset instead of holding it until the timeout. diff --git a/marketplace/storage/storage.go b/marketplace/storage/storage.go index 210be3112d..116f7b7787 100644 --- a/marketplace/storage/storage.go +++ b/marketplace/storage/storage.go @@ -679,7 +679,10 @@ func (s *MarketplaceStorage) BuyAssets( if !ok { return serrors.New("asset parameters would lead to integer overflow") } - + if uint64(costAcc) > maxPrice { + // we perform this check per asset to be able to terminate early + return serrors.New("assets too expensive") + } boughtIDs = append(boughtIDs, newAsset.ID) } _, err := tx.UpdateAccountMoney(ctx, accountID, -costAcc) From 66348f3c5e99f3811fa9c5a36963acd3216ad5f9 Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Mon, 12 Oct 2026 16:54:44 +0200 Subject: [PATCH 08/14] updated the update asset endpoint --- marketplace/db/sqlite.go | 210 ++++++--- marketplace/service.go | 40 +- marketplace/storage/storage.go | 99 ++++- marketplace_client/main.go | 60 +-- pkg/proto/hummingbird/marketplace.pb.go | 553 ++++++++++++++---------- proto/hummingbird/v1/marketplace.proto | 18 +- 6 files changed, 617 insertions(+), 363 deletions(-) diff --git a/marketplace/db/sqlite.go b/marketplace/db/sqlite.go index 90878b1a2e..cdb43fe09f 100644 --- a/marketplace/db/sqlite.go +++ b/marketplace/db/sqlite.go @@ -35,6 +35,7 @@ type Repository interface { InsertReservation(ctx context.Context, r *DBReservation) (int64, error) SearchOwnedAssets(ctx context.Context, params *OwnedAssetQuery) ([]*DBAsset, error) SearchListedAssets(ctx context.Context, params *ListedAssetQuery) ([]*DBAsset, error) + LookupAsset(ctx context.Context, id int64, ia addr.IA) (*DBAsset, error) FetchReservations(ctx context.Context, params *ReservationQuery) ([]*DBReservation, error) GetUser(ctx context.Context, id int64) (*DBUser, error) GetUserByName(ctx context.Context, name string) (*DBUser, error) @@ -66,7 +67,12 @@ type Repository interface { RegisterAssetEvent(ctx context.Context, a *DBAsset, eventType AssetEventType) (int64, error) TransitionAsset(ctx context.Context, assetID int64, accountID *int64, from AssetState, to AssetState) (*DBAsset, error) InsertAssetSegment(ctx context.Context, asset_id int64, from int64, to int64, bw uint32) (int64, error) - BuyAsset(ctx context.Context, assetId int64, startsAt int64, stopsAt int64, bw uint32, accountId int64) (*DBAsset, error) + CheckAssetAvailability(ctx context.Context, assetId int64, startsAt int64, stopsAt int64, bw uint32) (*DBAsset, error) + SplitAssetAt(ctx context.Context, assetId int64, point int64) error + MergeAssetAt(ctx context.Context, assetId int64, point int64) error + IncreaseAssetSegmentsBandwidth(ctx context.Context, assetId int64, delta int64) error + UpdateAsset(ctx context.Context, a *DBAsset) (int64, error) + BuyAsset(ctx context.Context, startsAt int64, stopsAt int64, bw uint32, accountId int64, dbAsset *DBAsset) (*DBAsset, error) } type MarketplaceDB interface { @@ -677,6 +683,21 @@ func (e *executor) SearchOwnedAssets(ctx context.Context, params *OwnedAssetQuer return res, nil } +func (e *executor) LookupAsset(ctx context.Context, id int64, ia addr.IA) (*DBAsset, error) { + if e.read == nil { + return nil, serrors.New("No database open") + } + q := `SELECT ` + assetColumnsWithAlias + ` FROM Assets a WHERE a.ID = ? AND a.isd_id = ? AND a.as_id = ?` + rows, err := e.read.QueryContext(ctx, q, id, ia.ISD(), ia.AS()) + if err != nil { + return nil, err + } + if !rows.Next() { + return nil, serrors.New("asset not found") + } + return scanAsset(rows) +} + func (e *executor) SearchListedAssets(ctx context.Context, params *ListedAssetQuery) ([]*DBAsset, error) { if e.read == nil { return nil, serrors.New("No database open") @@ -1361,41 +1382,43 @@ func (e *executor) InsertAssetSegment(ctx context.Context, asset_id int64, from return res.LastInsertId() } -// Buys a single asset. Must be called as part of a transaction -func (e *executor) BuyAsset( - ctx context.Context, - assetId int64, - startsAt int64, - stopsAt int64, - bw uint32, - accountId int64, -) (*DBAsset, error) { +func (e *executor) IncreaseAssetSegmentsBandwidth(ctx context.Context, assetId int64, delta int64) error { if e.write == nil { - return nil, serrors.New("No database open") + return serrors.New("No database open") } - splitAt := func(point int64) error { - insertInst := ` - INSERT INTO Asset_segment (asset_id, starts_at, stops_at, available) - SELECT asset_id, ?, stops_at, available - FROM Asset_segment - WHERE asset_id = ? AND starts_at < ? AND stops_at > ?` - _, err := e.write.ExecContext(ctx, insertInst, point, assetId, point, point) - if err != nil { - return err - } - updateInst := ` - UPDATE Asset_segment - SET stops_at = ? - WHERE asset_id = ? AND starts_at < ? AND stops_at > ? - ` - _, err = e.write.ExecContext(ctx, updateInst, point, assetId, point, point) - if err != nil { - return err - } - return nil + inst := ` + UPDATE Asset_Segment SET available = available + ? + WHERE asset_id = ?` + _, err := e.write.ExecContext(ctx, inst, delta, assetId) + if err != nil { + return err + } + return nil +} + +func (e *executor) UpdateAsset(ctx context.Context, a *DBAsset) (int64, error) { + if e.write == nil { + return 0, serrors.New("No database open") } - mergeAt := func(point int64) error { - updateInst := ` + inst := ` + UPDATE Assets + SET bandwidth = ?, bandwidth_min = ?, bandwidth_max = ?, price = ?, time_granularity = ?, + time_min_duration = ?, time_max_duration = ?, starts_at = ?, stops_at = ? + WHERE id = ? AND state = ? AND account_ID IS NULL` + res, err := e.write.ExecContext(ctx, inst, a.Bandwidth, a.BandwidthMin, a.BandwidthMax, + a.Price, a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, a.StartAt, + a.StopsAt, a.ID, AssetStateAvailable) + if err != nil { + return 0, err + } + return res.RowsAffected() +} + +func (e *executor) MergeAssetAt(ctx context.Context, assetId int64, point int64) error { + if e.write == nil { + return serrors.New("No database open") + } + updateInst := ` UPDATE Asset_Segment AS l SET stops_at = r.stops_at FROM Asset_Segment AS r @@ -1403,17 +1426,53 @@ func (e *executor) BuyAsset( AND r.asset_id = ? AND r.starts_at = ? AND r.available = l.available ` - res, err := e.write.ExecContext(ctx, updateInst, assetId, point, assetId, point) - if err != nil { - return fmt.Errorf("merge at %d (update): %w", point, err) - } - if n, _ := res.RowsAffected(); n == 1 { - deleteInst := `DELETE FROM Asset_Segment WHERE asset_id = ? AND starts_at = ?` - if _, err := e.write.ExecContext(ctx, deleteInst, assetId, point); err != nil { - return fmt.Errorf("merge at %d (delete): %w", point, err) - } + res, err := e.write.ExecContext(ctx, updateInst, assetId, point, assetId, point) + if err != nil { + return fmt.Errorf("merge at %d (update): %w", point, err) + } + if n, _ := res.RowsAffected(); n == 1 { + deleteInst := `DELETE FROM Asset_Segment WHERE asset_id = ? AND starts_at = ?` + if _, err := e.write.ExecContext(ctx, deleteInst, assetId, point); err != nil { + return fmt.Errorf("merge at %d (delete): %w", point, err) } - return nil + } + return nil +} + +func (e *executor) SplitAssetAt(ctx context.Context, assetId int64, point int64) error { + if e.write == nil { + return serrors.New("No database open") + } + insertInst := ` + INSERT INTO Asset_segment (asset_id, starts_at, stops_at, available) + SELECT asset_id, ?, stops_at, available + FROM Asset_segment + WHERE asset_id = ? AND starts_at < ? AND stops_at > ?` + _, err := e.write.ExecContext(ctx, insertInst, point, assetId, point, point) + if err != nil { + return err + } + updateInst := ` + UPDATE Asset_segment + SET stops_at = ? + WHERE asset_id = ? AND starts_at < ? AND stops_at > ? + ` + _, err = e.write.ExecContext(ctx, updateInst, point, assetId, point, point) + if err != nil { + return err + } + return nil +} + +func (e *executor) CheckAssetAvailability( + ctx context.Context, + assetId int64, + startsAt int64, + stopsAt int64, + bw uint32, +) (*DBAsset, error) { + if e.read == nil { + return nil, serrors.New("No database open") } inst := ` SELECT ` + assetColumnsWithAlias + ` FROM Assets a @@ -1427,7 +1486,7 @@ func (e *executor) BuyAsset( AND g.starts_at < ? AND g.stops_at > ? AND g.available < ?); ` - rows, err := e.write.QueryContext(ctx, inst, assetId, AssetStateAvailable, startsAt, stopsAt, stopsAt, startsAt, bw) + rows, err := e.read.QueryContext(ctx, inst, assetId, AssetStateAvailable, startsAt, stopsAt, stopsAt, startsAt, bw) if err != nil { return nil, err } @@ -1438,20 +1497,23 @@ func (e *executor) BuyAsset( if err != nil { return nil, err } + return asset, nil +} - err = splitAt(startsAt) - if err != nil { - return nil, err - } - err = splitAt(stopsAt) - if err != nil { - return nil, err - } +func (e *executor) BuyFromAssetSegment( + ctx context.Context, + assetId int64, + startsAt int64, + stopsAt int64, + bw uint32, + accountId int64, + asset *DBAsset, +) (*DBAsset, error) { decreaseAvailabilityInst := ` UPDATE asset_segment SET available = available - ? WHERE asset_id = ? AND starts_at >= ? AND stops_at <= ?` - _, err = e.write.ExecContext(ctx, decreaseAvailabilityInst, bw, assetId, startsAt, stopsAt) + _, err := e.write.ExecContext(ctx, decreaseAvailabilityInst, bw, assetId, startsAt, stopsAt) if err != nil { return nil, err } @@ -1478,13 +1540,51 @@ func (e *executor) BuyAsset( return nil, err } userAsset.ID = newId - err = mergeAt(startsAt) + return userAsset, nil +} + +// Buys a single asset. Availability must be checked in advance in same transaction. +func (e *executor) BuyAsset( + ctx context.Context, + startsAt int64, + stopsAt int64, + bw uint32, + accountId int64, + dbAsset *DBAsset, +) (*DBAsset, error) { + if e.write == nil { + return nil, serrors.New("No database open") + } + decreaseAvailabilityInst := ` + UPDATE asset_segment + SET available = available - ? + WHERE asset_id = ? AND starts_at >= ? AND stops_at <= ?` + _, err := e.write.ExecContext(ctx, decreaseAvailabilityInst, bw, dbAsset.ID, startsAt, stopsAt) if err != nil { return nil, err } - err = mergeAt(stopsAt) + userAsset := &DBAsset{ + AccountId: sql.NullInt64{ + Int64: accountId, + Valid: true, + }, + IA: dbAsset.IA, + Bandwidth: bw, + StartAt: startsAt, + StopsAt: stopsAt, + BandwidthMin: dbAsset.BandwidthMin, + BandwidthMax: dbAsset.BandwidthMax, + Price: dbAsset.Price, + TimeGranularity: dbAsset.TimeGranularity, + TimeMinDuration: dbAsset.TimeMinDuration, + TimeMaxDuration: dbAsset.TimeMaxDuration, + IfIdIngress: dbAsset.IfIdIngress, + IfIdEgress: dbAsset.IfIdEgress, + } + newId, err := e.InsertAsset(ctx, userAsset) if err != nil { return nil, err } + userAsset.ID = newId return userAsset, nil } diff --git a/marketplace/service.go b/marketplace/service.go index ebdadc8188..396e203f7a 100644 --- a/marketplace/service.go +++ b/marketplace/service.go @@ -286,35 +286,10 @@ func (s *Service) UpdateAssets(ctx context.Context, req *connect.Request[humming } return 0, nil case *hummingbird.AssetUpdate_Update: - dbAsset := &db.DBAsset{ - ID: assetId, - IA: ia, - Bandwidth: t.Update.Bandwidth, - BandwidthMin: t.Update.BandwidthMin, - BandwidthMax: t.Update.BandwidthMax, - StartAt: t.Update.StartsAt, - StopsAt: t.Update.StopsAt, - Price: t.Update.Price, - TimeGranularity: t.Update.TimeGranularity, - TimeMinDuration: t.Update.TimeMinDuration, - TimeMaxDuration: t.Update.TimeMaxDuration, - } - if time.Unix(dbAsset.StopsAt, 0).UTC().After(time.Now().Add(time.Duration(s.info.AssetValidityMax) * time.Second).UTC()) { + if t.Update.StopsAt != nil && time.Unix(*t.Update.StopsAt, 0).UTC().After(time.Now().Add(time.Duration(s.info.AssetValidityMax)*time.Second).UTC()) { return 0, serrors.New("validity outside allowed range") } - if t.Update.IfIdIngress != nil { - dbAsset.IfIdIngress = sql.NullInt32{ - Valid: true, - Int32: int32(*t.Update.IfIdIngress), - } - } - if t.Update.IfIdEgress != nil { - dbAsset.IfIdEgress = sql.NullInt32{ - Valid: true, - Int32: int32(*t.Update.IfIdEgress), - } - } - newId, err := s.store.UpdateListedAsset(ctx, dbAsset) + newId, err := s.store.UpdateListedAsset(ctx, assetId, ia, t.Update) if err != nil { return 0, err } @@ -325,18 +300,15 @@ func (s *Service) UpdateAssets(ctx context.Context, req *connect.Request[humming } res := make([]*hummingbird.UpdateAssetResult, 0, len(req.Msg.Assets)) for _, update := range req.Msg.Assets { - newId, err := handleAsset(update) + _, err := handleAsset(update) if err != nil { + errString := err.Error() res = append(res, &hummingbird.UpdateAssetResult{ - ResultType: &hummingbird.UpdateAssetResult_Error{ - Error: err.Error(), - }, + Error: &errString, }) } else { res = append(res, &hummingbird.UpdateAssetResult{ - ResultType: &hummingbird.UpdateAssetResult_NewId{ - NewId: protoAssetID(newId), - }, + Error: nil, }) } } diff --git a/marketplace/storage/storage.go b/marketplace/storage/storage.go index 116f7b7787..9fe258bc0d 100644 --- a/marketplace/storage/storage.go +++ b/marketplace/storage/storage.go @@ -118,22 +118,73 @@ func (s *MarketplaceStorage) PublishAsset( } func (s *MarketplaceStorage) UpdateListedAsset( ctx context.Context, - a *marketplacedb.DBAsset, + id int64, + ia addr.IA, + a *hummingbird.UpdateAsset, ) (int64, error) { - if err := validateAsset(a); err != nil { - return 0, err - } var newID int64 err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { - x, err := tx.DeleteListedAsset(ctx, a.IA, a.ID) + // get current dbAsset + dbAsset, err := tx.LookupAsset(ctx, id, ia) + if err != nil { + return err + } + // check for illegal modifications + if (a.Bandwidth != nil && *a.Bandwidth < dbAsset.Bandwidth) || + (a.StartsAt != nil && *a.StartsAt > dbAsset.StartAt) || + (a.StopsAt != nil && *a.StopsAt < dbAsset.StopsAt) { + return serrors.New("bandwidth and validity period cannot be decreased") + } + if a.Bandwidth != nil && *a.Bandwidth > dbAsset.Bandwidth { + if err = tx.IncreaseAssetSegmentsBandwidth(ctx, id, int64(*a.Bandwidth-dbAsset.Bandwidth)); err != nil { + return err + } + dbAsset.Bandwidth = *a.Bandwidth + } + if a.StartsAt != nil && *a.StartsAt < dbAsset.StartAt { + if _, err = tx.InsertAssetSegment(ctx, id, *a.StartsAt, dbAsset.StartAt, dbAsset.Bandwidth); err != nil { + return err + } + if err = tx.MergeAssetAt(ctx, id, dbAsset.StartAt); err != nil { + return err + } + dbAsset.StartAt = *a.StartsAt + } + if a.StopsAt != nil && *a.StopsAt > dbAsset.StopsAt { + if _, err = tx.InsertAssetSegment(ctx, id, dbAsset.StopsAt, *a.StopsAt, dbAsset.Bandwidth); err != nil { + return err + } + if err = tx.MergeAssetAt(ctx, id, dbAsset.StopsAt); err != nil { + return err + } + dbAsset.StopsAt = *a.StopsAt + } + if a.BandwidthMin != nil { + dbAsset.BandwidthMin = *a.BandwidthMin + } + if a.BandwidthMax != nil { + dbAsset.BandwidthMax = *a.BandwidthMax + } + if a.TimeMinDuration != nil { + dbAsset.TimeMinDuration = *a.TimeMinDuration + } + if a.TimeMaxDuration != nil { + dbAsset.TimeMaxDuration = *a.TimeMaxDuration + } + if a.TimeGranularity != nil { + dbAsset.TimeGranularity = *a.TimeGranularity + } + if a.Price != nil { + dbAsset.Price = *a.Price + } + x, err := tx.UpdateAsset(ctx, dbAsset) if err != nil { return err } if x != 1 { - return serrors.New("no modifiable asset with that ID found") + return serrors.New("invalid assets") } - newID, err = tx.InsertAsset(ctx, a) - return err + return nil }) return newID, err } @@ -650,6 +701,7 @@ func (s *MarketplaceStorage) BuyAssets( } boughtIDs := []int64{} costAcc := int64(0) + err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { calcCost := func(a *marketplacedb.DBAsset) (int64, bool) { totalAssetPrice, ok := multiplyInts(int64(a.StopsAt-a.StartAt), @@ -661,13 +713,40 @@ func (s *MarketplaceStorage) BuyAssets( int64(s.transactionFeeAbsolute) return addInt64(int64(totalAssetPrice), fee) } + buyAsset := func( + assetId int64, + startsAt int64, + stopsAt int64, + bw uint32, + ) (*marketplacedb.DBAsset, error) { + asset, err := tx.CheckAssetAvailability(ctx, assetId, startsAt, stopsAt, bw) + if err != nil { + return nil, err + } + if err = tx.SplitAssetAt(ctx, assetId, startsAt); err != nil { + return nil, err + } + if err = tx.SplitAssetAt(ctx, assetId, stopsAt); err != nil { + return nil, err + } + userAsset, err := tx.BuyAsset(ctx, startsAt, stopsAt, bw, accountID, asset) + if err != nil { + return nil, err + } + if err = tx.MergeAssetAt(ctx, assetId, startsAt); err != nil { + return nil, err + } + if err = tx.MergeAssetAt(ctx, assetId, stopsAt); err != nil { + return nil, err + } + return userAsset, nil + } for _, asset := range assets { assetId, err := DatabaseAssetID(asset.AssetId) if err != nil { return err } - newAsset, err := tx.BuyAsset(ctx, assetId, asset.StartsAtExactly, asset.StopsAtExactly, - asset.BandwidthExact, accountID) + newAsset, err := buyAsset(assetId, asset.StartsAtExactly, asset.StopsAtExactly, asset.BandwidthExact) if err != nil { return err } diff --git a/marketplace_client/main.go b/marketplace_client/main.go index 3404c98d28..14d6e75bff 100644 --- a/marketplace_client/main.go +++ b/marketplace_client/main.go @@ -484,22 +484,35 @@ func handleUpdate( fmt.Println(err) continue } + bw := readOptionalUint32(reader, "bandwidth: ") + bwMin := readOptionalUint32(reader, "minimum bandwidth: ") + bwMax := readOptionalUint32(reader, "maximum bandwidth: ") + startsAt := readOptionalTime(reader, "Starts at (2026-06-23T13:25:36Z): ") + var startsAtUnix *int64 + if startsAt != nil { + tmp := startsAt.Unix() + startsAtUnix = &tmp + } + stopsAt := readOptionalTime(reader, "Stops at (2026-06-23T13:25:36Z): ") + var stopsAtUnix *int64 + if stopsAt != nil { + tmp := stopsAt.Unix() + stopsAtUnix = &tmp + } assetUpdates = append(assetUpdates, &hummingbird.AssetUpdate{ AssetId: assetId, Operation: &hummingbird.AssetUpdate_Update{ - Update: &hummingbird.PublisherAsset{ - IfIdIngress: readOptionalUint32(reader, "ingress: "), - IfIdEgress: readOptionalUint32(reader, "egress: "), - Bandwidth: uint32(readUint64(reader, "bandwidth: ")), - BandwidthMin: uint32(readUint64(reader, "minimum bandwidth: ")), - BandwidthMax: uint32(readUint64(reader, "maximum bandwidth: ")), - StartsAt: readTime(reader, "Starts at (2026-06-23T13:25:36Z): ").Unix(), - StopsAt: readTime(reader, "Stops at (2026-06-23T13:25:36Z): ").Unix(), - Price: uint32(readUint64( - reader, "price per kbit per second: ")), - TimeMinDuration: uint32(readUint64(reader, "minimum time duration: ")), - TimeMaxDuration: uint32(readUint64(reader, "maximum time duration: ")), - TimeGranularity: uint32(readUint64(reader, "time granularity: "))}, + Update: &hummingbird.UpdateAsset{ + Bandwidth: bw, + BandwidthMin: bwMin, + BandwidthMax: bwMax, + StartsAt: startsAtUnix, + StopsAt: stopsAtUnix, + Price: readOptionalUint32(reader, "price per kbit per second: "), + TimeMinDuration: readOptionalUint32(reader, "minimum time duration: "), + TimeMaxDuration: readOptionalUint32(reader, "maximum time duration: "), + TimeGranularity: readOptionalUint32(reader, "time granularity: "), + }, }, }) default: @@ -535,21 +548,14 @@ func handleUpdate( if i >= len(assetUpdates) { break } - switch t := res.ResultType.(type) { - case *hummingbird.UpdateAssetResult_NewId: - if t.NewId == nil { - fmt.Printf("%s: deleted\n", hex.EncodeToString(assetUpdates[i].AssetId)) - } else { - fmt.Printf("%s: updated to -> %s\n", - hex.EncodeToString(assetUpdates[i].AssetId), - hex.EncodeToString(t.NewId)) - } - case *hummingbird.UpdateAssetResult_Error: - fmt.Printf("%s: error: %s\n", - hex.EncodeToString(assetUpdates[i].AssetId), t.Error) - default: - fmt.Printf("%s: unknown result", + if res.Error == nil { + fmt.Printf("Successfully updated / deleted %s\n", hex.EncodeToString(assetUpdates[i].AssetId)) + } else { + fmt.Printf("Error updating / deleting %s: %s\n", + hex.EncodeToString(assetUpdates[i].AssetId), + *res.Error, + ) } } return diff --git a/pkg/proto/hummingbird/marketplace.pb.go b/pkg/proto/hummingbird/marketplace.pb.go index b39f15545c..87e427f46d 100644 --- a/pkg/proto/hummingbird/marketplace.pb.go +++ b/pkg/proto/hummingbird/marketplace.pb.go @@ -213,7 +213,7 @@ func (x *AssetUpdate) GetOperation() isAssetUpdate_Operation { return nil } -func (x *AssetUpdate) GetUpdate() *PublisherAsset { +func (x *AssetUpdate) GetUpdate() *UpdateAsset { if x != nil { if x, ok := x.Operation.(*AssetUpdate_Update); ok { return x.Update @@ -236,7 +236,7 @@ type isAssetUpdate_Operation interface { } type AssetUpdate_Update struct { - Update *PublisherAsset `protobuf:"bytes,2,opt,name=update,proto3,oneof"` + Update *UpdateAsset `protobuf:"bytes,2,opt,name=update,proto3,oneof"` } type AssetUpdate_Remove struct { @@ -247,6 +247,114 @@ func (*AssetUpdate_Update) isAssetUpdate_Operation() {} func (*AssetUpdate_Remove) isAssetUpdate_Operation() {} +type UpdateAsset struct { + state protoimpl.MessageState `protogen:"open.v1"` + Bandwidth *uint32 `protobuf:"varint,1,opt,name=bandwidth,proto3,oneof" json:"bandwidth,omitempty"` + BandwidthMin *uint32 `protobuf:"varint,2,opt,name=bandwidth_min,json=bandwidthMin,proto3,oneof" json:"bandwidth_min,omitempty"` + BandwidthMax *uint32 `protobuf:"varint,3,opt,name=bandwidth_max,json=bandwidthMax,proto3,oneof" json:"bandwidth_max,omitempty"` + StartsAt *int64 `protobuf:"varint,4,opt,name=starts_at,json=startsAt,proto3,oneof" json:"starts_at,omitempty"` + StopsAt *int64 `protobuf:"varint,5,opt,name=stops_at,json=stopsAt,proto3,oneof" json:"stops_at,omitempty"` + TimeGranularity *uint32 `protobuf:"varint,6,opt,name=time_granularity,json=timeGranularity,proto3,oneof" json:"time_granularity,omitempty"` + TimeMinDuration *uint32 `protobuf:"varint,7,opt,name=time_min_duration,json=timeMinDuration,proto3,oneof" json:"time_min_duration,omitempty"` + TimeMaxDuration *uint32 `protobuf:"varint,8,opt,name=time_max_duration,json=timeMaxDuration,proto3,oneof" json:"time_max_duration,omitempty"` + Price *uint32 `protobuf:"varint,9,opt,name=price,proto3,oneof" json:"price,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAsset) Reset() { + *x = UpdateAsset{} + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAsset) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAsset) ProtoMessage() {} + +func (x *UpdateAsset) ProtoReflect() protoreflect.Message { + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateAsset.ProtoReflect.Descriptor instead. +func (*UpdateAsset) Descriptor() ([]byte, []int) { + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{2} +} + +func (x *UpdateAsset) GetBandwidth() uint32 { + if x != nil && x.Bandwidth != nil { + return *x.Bandwidth + } + return 0 +} + +func (x *UpdateAsset) GetBandwidthMin() uint32 { + if x != nil && x.BandwidthMin != nil { + return *x.BandwidthMin + } + return 0 +} + +func (x *UpdateAsset) GetBandwidthMax() uint32 { + if x != nil && x.BandwidthMax != nil { + return *x.BandwidthMax + } + return 0 +} + +func (x *UpdateAsset) GetStartsAt() int64 { + if x != nil && x.StartsAt != nil { + return *x.StartsAt + } + return 0 +} + +func (x *UpdateAsset) GetStopsAt() int64 { + if x != nil && x.StopsAt != nil { + return *x.StopsAt + } + return 0 +} + +func (x *UpdateAsset) GetTimeGranularity() uint32 { + if x != nil && x.TimeGranularity != nil { + return *x.TimeGranularity + } + return 0 +} + +func (x *UpdateAsset) GetTimeMinDuration() uint32 { + if x != nil && x.TimeMinDuration != nil { + return *x.TimeMinDuration + } + return 0 +} + +func (x *UpdateAsset) GetTimeMaxDuration() uint32 { + if x != nil && x.TimeMaxDuration != nil { + return *x.TimeMaxDuration + } + return 0 +} + +func (x *UpdateAsset) GetPrice() uint32 { + if x != nil && x.Price != nil { + return *x.Price + } + return 0 +} + type Remove struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -255,7 +363,7 @@ type Remove struct { func (x *Remove) Reset() { *x = Remove{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[2] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -267,7 +375,7 @@ func (x *Remove) String() string { func (*Remove) ProtoMessage() {} func (x *Remove) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[2] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -280,7 +388,7 @@ func (x *Remove) ProtoReflect() protoreflect.Message { // Deprecated: Use Remove.ProtoReflect.Descriptor instead. func (*Remove) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{2} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{3} } type UpdateAssetsResponse struct { @@ -292,7 +400,7 @@ type UpdateAssetsResponse struct { func (x *UpdateAssetsResponse) Reset() { *x = UpdateAssetsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[3] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -304,7 +412,7 @@ func (x *UpdateAssetsResponse) String() string { func (*UpdateAssetsResponse) ProtoMessage() {} func (x *UpdateAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[3] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -317,7 +425,7 @@ func (x *UpdateAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateAssetsResponse.ProtoReflect.Descriptor instead. func (*UpdateAssetsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{3} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{4} } func (x *UpdateAssetsResponse) GetResult() []*UpdateAssetResult { @@ -328,19 +436,15 @@ func (x *UpdateAssetsResponse) GetResult() []*UpdateAssetResult { } type UpdateAssetResult struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to ResultType: - // - // *UpdateAssetResult_NewId - // *UpdateAssetResult_Error - ResultType isUpdateAssetResult_ResultType `protobuf_oneof:"result_type"` + state protoimpl.MessageState `protogen:"open.v1"` + Error *string `protobuf:"bytes,1,opt,name=error,proto3,oneof" json:"error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateAssetResult) Reset() { *x = UpdateAssetResult{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[4] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -352,7 +456,7 @@ func (x *UpdateAssetResult) String() string { func (*UpdateAssetResult) ProtoMessage() {} func (x *UpdateAssetResult) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[4] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -365,50 +469,16 @@ func (x *UpdateAssetResult) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateAssetResult.ProtoReflect.Descriptor instead. func (*UpdateAssetResult) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{4} -} - -func (x *UpdateAssetResult) GetResultType() isUpdateAssetResult_ResultType { - if x != nil { - return x.ResultType - } - return nil -} - -func (x *UpdateAssetResult) GetNewId() []byte { - if x != nil { - if x, ok := x.ResultType.(*UpdateAssetResult_NewId); ok { - return x.NewId - } - } - return nil + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{5} } func (x *UpdateAssetResult) GetError() string { - if x != nil { - if x, ok := x.ResultType.(*UpdateAssetResult_Error); ok { - return x.Error - } + if x != nil && x.Error != nil { + return *x.Error } return "" } -type isUpdateAssetResult_ResultType interface { - isUpdateAssetResult_ResultType() -} - -type UpdateAssetResult_NewId struct { - NewId []byte `protobuf:"bytes,1,opt,name=new_id,json=newId,proto3,oneof"` -} - -type UpdateAssetResult_Error struct { - Error string `protobuf:"bytes,2,opt,name=error,proto3,oneof"` -} - -func (*UpdateAssetResult_NewId) isUpdateAssetResult_ResultType() {} - -func (*UpdateAssetResult_Error) isUpdateAssetResult_ResultType() {} - type StatisticsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` IfIdIngress *uint32 `protobuf:"varint,1,opt,name=if_id_ingress,json=ifIdIngress,proto3,oneof" json:"if_id_ingress,omitempty"` @@ -422,7 +492,7 @@ type StatisticsRequest struct { func (x *StatisticsRequest) Reset() { *x = StatisticsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[5] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -434,7 +504,7 @@ func (x *StatisticsRequest) String() string { func (*StatisticsRequest) ProtoMessage() {} func (x *StatisticsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[5] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -447,7 +517,7 @@ func (x *StatisticsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatisticsRequest.ProtoReflect.Descriptor instead. func (*StatisticsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{5} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{6} } func (x *StatisticsRequest) GetIfIdIngress() uint32 { @@ -494,7 +564,7 @@ type StatisticsResponse struct { func (x *StatisticsResponse) Reset() { *x = StatisticsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[6] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -506,7 +576,7 @@ func (x *StatisticsResponse) String() string { func (*StatisticsResponse) ProtoMessage() {} func (x *StatisticsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[6] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -519,7 +589,7 @@ func (x *StatisticsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatisticsResponse.ProtoReflect.Descriptor instead. func (*StatisticsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{6} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{7} } func (x *StatisticsResponse) GetStatistics() []*StatisticsResponseEntry { @@ -539,7 +609,7 @@ type StatisticsResponseEntry struct { func (x *StatisticsResponseEntry) Reset() { *x = StatisticsResponseEntry{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[7] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -551,7 +621,7 @@ func (x *StatisticsResponseEntry) String() string { func (*StatisticsResponseEntry) ProtoMessage() {} func (x *StatisticsResponseEntry) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[7] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -564,7 +634,7 @@ func (x *StatisticsResponseEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use StatisticsResponseEntry.ProtoReflect.Descriptor instead. func (*StatisticsResponseEntry) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{7} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{8} } func (x *StatisticsResponseEntry) GetRevenue() uint64 { @@ -595,7 +665,7 @@ type SplitAssetRequest struct { func (x *SplitAssetRequest) Reset() { *x = SplitAssetRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[8] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -607,7 +677,7 @@ func (x *SplitAssetRequest) String() string { func (*SplitAssetRequest) ProtoMessage() {} func (x *SplitAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[8] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -620,7 +690,7 @@ func (x *SplitAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SplitAssetRequest.ProtoReflect.Descriptor instead. func (*SplitAssetRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{8} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{9} } func (x *SplitAssetRequest) GetAssetId() []byte { @@ -680,7 +750,7 @@ type SplitBwOption struct { func (x *SplitBwOption) Reset() { *x = SplitBwOption{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[9] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -692,7 +762,7 @@ func (x *SplitBwOption) String() string { func (*SplitBwOption) ProtoMessage() {} func (x *SplitBwOption) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[9] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -705,7 +775,7 @@ func (x *SplitBwOption) ProtoReflect() protoreflect.Message { // Deprecated: Use SplitBwOption.ProtoReflect.Descriptor instead. func (*SplitBwOption) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{9} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{10} } func (x *SplitBwOption) GetSplits() []uint32 { @@ -724,7 +794,7 @@ type SplitTimeOption struct { func (x *SplitTimeOption) Reset() { *x = SplitTimeOption{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[10] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -736,7 +806,7 @@ func (x *SplitTimeOption) String() string { func (*SplitTimeOption) ProtoMessage() {} func (x *SplitTimeOption) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[10] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -749,7 +819,7 @@ func (x *SplitTimeOption) ProtoReflect() protoreflect.Message { // Deprecated: Use SplitTimeOption.ProtoReflect.Descriptor instead. func (*SplitTimeOption) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{10} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{11} } func (x *SplitTimeOption) GetSplits() []int64 { @@ -768,7 +838,7 @@ type SplitAssetResponse struct { func (x *SplitAssetResponse) Reset() { *x = SplitAssetResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[11] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -780,7 +850,7 @@ func (x *SplitAssetResponse) String() string { func (*SplitAssetResponse) ProtoMessage() {} func (x *SplitAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[11] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -793,7 +863,7 @@ func (x *SplitAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SplitAssetResponse.ProtoReflect.Descriptor instead. func (*SplitAssetResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{11} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{12} } func (x *SplitAssetResponse) GetAssetIds() [][]byte { @@ -812,7 +882,7 @@ type CombineAssetRequest struct { func (x *CombineAssetRequest) Reset() { *x = CombineAssetRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[12] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -824,7 +894,7 @@ func (x *CombineAssetRequest) String() string { func (*CombineAssetRequest) ProtoMessage() {} func (x *CombineAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[12] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -837,7 +907,7 @@ func (x *CombineAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CombineAssetRequest.ProtoReflect.Descriptor instead. func (*CombineAssetRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{12} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{13} } func (x *CombineAssetRequest) GetAssetIds() [][]byte { @@ -856,7 +926,7 @@ type CombineAssetResponse struct { func (x *CombineAssetResponse) Reset() { *x = CombineAssetResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[13] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -868,7 +938,7 @@ func (x *CombineAssetResponse) String() string { func (*CombineAssetResponse) ProtoMessage() {} func (x *CombineAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[13] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -881,7 +951,7 @@ func (x *CombineAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CombineAssetResponse.ProtoReflect.Descriptor instead. func (*CombineAssetResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{13} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{14} } func (x *CombineAssetResponse) GetAssetId() []byte { @@ -899,7 +969,7 @@ type MarketplaceInfoRequest struct { func (x *MarketplaceInfoRequest) Reset() { *x = MarketplaceInfoRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[14] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -911,7 +981,7 @@ func (x *MarketplaceInfoRequest) String() string { func (*MarketplaceInfoRequest) ProtoMessage() {} func (x *MarketplaceInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[14] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -924,7 +994,7 @@ func (x *MarketplaceInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarketplaceInfoRequest.ProtoReflect.Descriptor instead. func (*MarketplaceInfoRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{14} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{15} } type MarketplaceInfoResponse struct { @@ -947,7 +1017,7 @@ type MarketplaceInfoResponse struct { func (x *MarketplaceInfoResponse) Reset() { *x = MarketplaceInfoResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[15] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -959,7 +1029,7 @@ func (x *MarketplaceInfoResponse) String() string { func (*MarketplaceInfoResponse) ProtoMessage() {} func (x *MarketplaceInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[15] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -972,7 +1042,7 @@ func (x *MarketplaceInfoResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarketplaceInfoResponse.ProtoReflect.Descriptor instead. func (*MarketplaceInfoResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{15} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{16} } func (x *MarketplaceInfoResponse) GetApiMajorVersion() uint32 { @@ -1068,7 +1138,7 @@ type PublishAssetRequest struct { func (x *PublishAssetRequest) Reset() { *x = PublishAssetRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[16] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1080,7 +1150,7 @@ func (x *PublishAssetRequest) String() string { func (*PublishAssetRequest) ProtoMessage() {} func (x *PublishAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[16] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1093,7 +1163,7 @@ func (x *PublishAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PublishAssetRequest.ProtoReflect.Descriptor instead. func (*PublishAssetRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{16} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{17} } func (x *PublishAssetRequest) GetAsset() *PublisherAsset { @@ -1112,7 +1182,7 @@ type PublishAssetResponse struct { func (x *PublishAssetResponse) Reset() { *x = PublishAssetResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[17] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1124,7 +1194,7 @@ func (x *PublishAssetResponse) String() string { func (*PublishAssetResponse) ProtoMessage() {} func (x *PublishAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[17] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1137,7 +1207,7 @@ func (x *PublishAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PublishAssetResponse.ProtoReflect.Descriptor instead. func (*PublishAssetResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{17} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{18} } func (x *PublishAssetResponse) GetAssetId() []byte { @@ -1166,7 +1236,7 @@ type PublisherAsset struct { func (x *PublisherAsset) Reset() { *x = PublisherAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[18] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1178,7 +1248,7 @@ func (x *PublisherAsset) String() string { func (*PublisherAsset) ProtoMessage() {} func (x *PublisherAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[18] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1191,7 +1261,7 @@ func (x *PublisherAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use PublisherAsset.ProtoReflect.Descriptor instead. func (*PublisherAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{18} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{19} } func (x *PublisherAsset) GetIfIdIngress() uint32 { @@ -1285,7 +1355,7 @@ type SearchAssetsRequest struct { func (x *SearchAssetsRequest) Reset() { *x = SearchAssetsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[19] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1297,7 +1367,7 @@ func (x *SearchAssetsRequest) String() string { func (*SearchAssetsRequest) ProtoMessage() {} func (x *SearchAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[19] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1310,7 +1380,7 @@ func (x *SearchAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchAssetsRequest.ProtoReflect.Descriptor instead. func (*SearchAssetsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{19} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{20} } func (x *SearchAssetsRequest) GetRequest() isSearchAssetsRequest_Request { @@ -1376,7 +1446,7 @@ type SearchOwnedAssetsRequest struct { func (x *SearchOwnedAssetsRequest) Reset() { *x = SearchOwnedAssetsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1388,7 +1458,7 @@ func (x *SearchOwnedAssetsRequest) String() string { func (*SearchOwnedAssetsRequest) ProtoMessage() {} func (x *SearchOwnedAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[20] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1401,7 +1471,7 @@ func (x *SearchOwnedAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchOwnedAssetsRequest.ProtoReflect.Descriptor instead. func (*SearchOwnedAssetsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{20} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{21} } func (x *SearchOwnedAssetsRequest) GetIa() uint64 { @@ -1468,7 +1538,7 @@ type SearchListedAssetsRequest struct { func (x *SearchListedAssetsRequest) Reset() { *x = SearchListedAssetsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1480,7 +1550,7 @@ func (x *SearchListedAssetsRequest) String() string { func (*SearchListedAssetsRequest) ProtoMessage() {} func (x *SearchListedAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[21] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1493,7 +1563,7 @@ func (x *SearchListedAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchListedAssetsRequest.ProtoReflect.Descriptor instead. func (*SearchListedAssetsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{21} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{22} } func (x *SearchListedAssetsRequest) GetIa() uint64 { @@ -1554,7 +1624,7 @@ type SearchAssetsResponse struct { func (x *SearchAssetsResponse) Reset() { *x = SearchAssetsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1566,7 +1636,7 @@ func (x *SearchAssetsResponse) String() string { func (*SearchAssetsResponse) ProtoMessage() {} func (x *SearchAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[22] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1579,7 +1649,7 @@ func (x *SearchAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchAssetsResponse.ProtoReflect.Descriptor instead. func (*SearchAssetsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{22} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{23} } func (x *SearchAssetsResponse) GetAssets() []*SearchAsset { @@ -1608,7 +1678,7 @@ type SearchAsset struct { func (x *SearchAsset) Reset() { *x = SearchAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1620,7 +1690,7 @@ func (x *SearchAsset) String() string { func (*SearchAsset) ProtoMessage() {} func (x *SearchAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[23] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1633,7 +1703,7 @@ func (x *SearchAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchAsset.ProtoReflect.Descriptor instead. func (*SearchAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{23} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{24} } func (x *SearchAsset) GetAssetId() []byte { @@ -1724,7 +1794,7 @@ type SearchAssetInterval struct { func (x *SearchAssetInterval) Reset() { *x = SearchAssetInterval{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1736,7 +1806,7 @@ func (x *SearchAssetInterval) String() string { func (*SearchAssetInterval) ProtoMessage() {} func (x *SearchAssetInterval) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[24] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1749,7 +1819,7 @@ func (x *SearchAssetInterval) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchAssetInterval.ProtoReflect.Descriptor instead. func (*SearchAssetInterval) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{24} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{25} } func (x *SearchAssetInterval) GetBandwidth() uint32 { @@ -1783,7 +1853,7 @@ type BuyAssetsRequest struct { func (x *BuyAssetsRequest) Reset() { *x = BuyAssetsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1795,7 +1865,7 @@ func (x *BuyAssetsRequest) String() string { func (*BuyAssetsRequest) ProtoMessage() {} func (x *BuyAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[25] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1808,7 +1878,7 @@ func (x *BuyAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAssetsRequest.ProtoReflect.Descriptor instead. func (*BuyAssetsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{25} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{26} } func (x *BuyAssetsRequest) GetAssets() []*BuyAsset { @@ -1837,7 +1907,7 @@ type BuyAsset struct { func (x *BuyAsset) Reset() { *x = BuyAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1849,7 +1919,7 @@ func (x *BuyAsset) String() string { func (*BuyAsset) ProtoMessage() {} func (x *BuyAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[26] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1862,7 +1932,7 @@ func (x *BuyAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAsset.ProtoReflect.Descriptor instead. func (*BuyAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{26} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{27} } func (x *BuyAsset) GetAssetId() []byte { @@ -1903,7 +1973,7 @@ type BuyAssetsResponse struct { func (x *BuyAssetsResponse) Reset() { *x = BuyAssetsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1915,7 +1985,7 @@ func (x *BuyAssetsResponse) String() string { func (*BuyAssetsResponse) ProtoMessage() {} func (x *BuyAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[27] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1928,7 +1998,7 @@ func (x *BuyAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BuyAssetsResponse.ProtoReflect.Descriptor instead. func (*BuyAssetsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{27} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{28} } func (x *BuyAssetsResponse) GetAssets() []*BoughtAsset { @@ -1954,7 +2024,7 @@ type BoughtAsset struct { func (x *BoughtAsset) Reset() { *x = BoughtAsset{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1966,7 +2036,7 @@ func (x *BoughtAsset) String() string { func (*BoughtAsset) ProtoMessage() {} func (x *BoughtAsset) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[28] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1979,7 +2049,7 @@ func (x *BoughtAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use BoughtAsset.ProtoReflect.Descriptor instead. func (*BoughtAsset) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{28} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{29} } func (x *BoughtAsset) GetAssetId() []byte { @@ -2002,7 +2072,7 @@ type RedeemAssetRequest struct { func (x *RedeemAssetRequest) Reset() { *x = RedeemAssetRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2014,7 +2084,7 @@ func (x *RedeemAssetRequest) String() string { func (*RedeemAssetRequest) ProtoMessage() {} func (x *RedeemAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[29] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2027,7 +2097,7 @@ func (x *RedeemAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemAssetRequest.ProtoReflect.Descriptor instead. func (*RedeemAssetRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{29} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{30} } func (x *RedeemAssetRequest) GetInterfaces() isRedeemAssetRequest_Interfaces { @@ -2081,7 +2151,7 @@ type IngressEgressPair struct { func (x *IngressEgressPair) Reset() { *x = IngressEgressPair{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2093,7 +2163,7 @@ func (x *IngressEgressPair) String() string { func (*IngressEgressPair) ProtoMessage() {} func (x *IngressEgressPair) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[30] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2106,7 +2176,7 @@ func (x *IngressEgressPair) ProtoReflect() protoreflect.Message { // Deprecated: Use IngressEgressPair.ProtoReflect.Descriptor instead. func (*IngressEgressPair) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{30} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{31} } func (x *IngressEgressPair) GetIngressAssetId() []byte { @@ -2135,7 +2205,7 @@ type RedeemAssetResponse struct { func (x *RedeemAssetResponse) Reset() { *x = RedeemAssetResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2147,7 +2217,7 @@ func (x *RedeemAssetResponse) String() string { func (*RedeemAssetResponse) ProtoMessage() {} func (x *RedeemAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[31] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2160,7 +2230,7 @@ func (x *RedeemAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemAssetResponse.ProtoReflect.Descriptor instead. func (*RedeemAssetResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{31} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{32} } func (x *RedeemAssetResponse) GetAuthenticationKey() []byte { @@ -2205,7 +2275,7 @@ type FetchReservationsRequest struct { func (x *FetchReservationsRequest) Reset() { *x = FetchReservationsRequest{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2217,7 +2287,7 @@ func (x *FetchReservationsRequest) String() string { func (*FetchReservationsRequest) ProtoMessage() {} func (x *FetchReservationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[32] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2230,7 +2300,7 @@ func (x *FetchReservationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchReservationsRequest.ProtoReflect.Descriptor instead. func (*FetchReservationsRequest) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{32} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{33} } func (x *FetchReservationsRequest) GetIa() uint64 { @@ -2284,7 +2354,7 @@ type FetchReservationsResponse struct { func (x *FetchReservationsResponse) Reset() { *x = FetchReservationsResponse{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2296,7 +2366,7 @@ func (x *FetchReservationsResponse) String() string { func (*FetchReservationsResponse) ProtoMessage() {} func (x *FetchReservationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[33] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2309,7 +2379,7 @@ func (x *FetchReservationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchReservationsResponse.ProtoReflect.Descriptor instead. func (*FetchReservationsResponse) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{33} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{34} } func (x *FetchReservationsResponse) GetReservations() []*Reservation { @@ -2336,7 +2406,7 @@ type Reservation struct { func (x *Reservation) Reset() { *x = Reservation{} - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[34] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2348,7 +2418,7 @@ func (x *Reservation) String() string { func (*Reservation) ProtoMessage() {} func (x *Reservation) ProtoReflect() protoreflect.Message { - mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[34] + mi := &file_proto_hummingbird_v1_marketplace_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2361,7 +2431,7 @@ func (x *Reservation) ProtoReflect() protoreflect.Message { // Deprecated: Use Reservation.ProtoReflect.Descriptor instead. func (*Reservation) Descriptor() ([]byte, []int) { - return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{34} + return file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP(), []int{35} } func (x *Reservation) GetReservationId() uint32 { @@ -2433,19 +2503,39 @@ const file_proto_hummingbird_v1_marketplace_proto_rawDesc = "" + "\n" + "&proto/hummingbird/v1/marketplace.proto\x12\x14proto.hummingbird.v1\"P\n" + "\x13UpdateAssetsRequest\x129\n" + - "\x06assets\x18\x01 \x03(\v2!.proto.hummingbird.v1.AssetUpdateR\x06assets\"\xad\x01\n" + + "\x06assets\x18\x01 \x03(\v2!.proto.hummingbird.v1.AssetUpdateR\x06assets\"\xaa\x01\n" + "\vAssetUpdate\x12\x19\n" + - "\basset_id\x18\x01 \x01(\fR\aassetId\x12>\n" + - "\x06update\x18\x02 \x01(\v2$.proto.hummingbird.v1.PublisherAssetH\x00R\x06update\x126\n" + + "\basset_id\x18\x01 \x01(\fR\aassetId\x12;\n" + + "\x06update\x18\x02 \x01(\v2!.proto.hummingbird.v1.UpdateAssetH\x00R\x06update\x126\n" + "\x06remove\x18\x03 \x01(\v2\x1c.proto.hummingbird.v1.RemoveH\x00R\x06removeB\v\n" + - "\toperation\"\b\n" + + "\toperation\"\x8b\x04\n" + + "\vUpdateAsset\x12!\n" + + "\tbandwidth\x18\x01 \x01(\rH\x00R\tbandwidth\x88\x01\x01\x12(\n" + + "\rbandwidth_min\x18\x02 \x01(\rH\x01R\fbandwidthMin\x88\x01\x01\x12(\n" + + "\rbandwidth_max\x18\x03 \x01(\rH\x02R\fbandwidthMax\x88\x01\x01\x12 \n" + + "\tstarts_at\x18\x04 \x01(\x03H\x03R\bstartsAt\x88\x01\x01\x12\x1e\n" + + "\bstops_at\x18\x05 \x01(\x03H\x04R\astopsAt\x88\x01\x01\x12.\n" + + "\x10time_granularity\x18\x06 \x01(\rH\x05R\x0ftimeGranularity\x88\x01\x01\x12/\n" + + "\x11time_min_duration\x18\a \x01(\rH\x06R\x0ftimeMinDuration\x88\x01\x01\x12/\n" + + "\x11time_max_duration\x18\b \x01(\rH\aR\x0ftimeMaxDuration\x88\x01\x01\x12\x19\n" + + "\x05price\x18\t \x01(\rH\bR\x05price\x88\x01\x01B\f\n" + + "\n" + + "_bandwidthB\x10\n" + + "\x0e_bandwidth_minB\x10\n" + + "\x0e_bandwidth_maxB\f\n" + + "\n" + + "_starts_atB\v\n" + + "\t_stops_atB\x13\n" + + "\x11_time_granularityB\x14\n" + + "\x12_time_min_durationB\x14\n" + + "\x12_time_max_durationB\b\n" + + "\x06_price\"\b\n" + "\x06Remove\"W\n" + "\x14UpdateAssetsResponse\x12?\n" + - "\x06result\x18\x01 \x03(\v2'.proto.hummingbird.v1.UpdateAssetResultR\x06result\"S\n" + - "\x11UpdateAssetResult\x12\x17\n" + - "\x06new_id\x18\x01 \x01(\fH\x00R\x05newId\x12\x16\n" + - "\x05error\x18\x02 \x01(\tH\x00R\x05errorB\r\n" + - "\vresult_type\"\xc2\x01\n" + + "\x06result\x18\x01 \x03(\v2'.proto.hummingbird.v1.UpdateAssetResultR\x06result\"8\n" + + "\x11UpdateAssetResult\x12\x19\n" + + "\x05error\x18\x01 \x01(\tH\x00R\x05error\x88\x01\x01B\b\n" + + "\x06_error\"\xc2\x01\n" + "\x11StatisticsRequest\x12'\n" + "\rif_id_ingress\x18\x01 \x01(\rH\x00R\vifIdIngress\x88\x01\x01\x12%\n" + "\fif_id_egress\x18\x02 \x01(\rH\x01R\n" + @@ -2657,85 +2747,86 @@ func file_proto_hummingbird_v1_marketplace_proto_rawDescGZIP() []byte { } var file_proto_hummingbird_v1_marketplace_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_proto_hummingbird_v1_marketplace_proto_msgTypes = make([]protoimpl.MessageInfo, 35) +var file_proto_hummingbird_v1_marketplace_proto_msgTypes = make([]protoimpl.MessageInfo, 36) var file_proto_hummingbird_v1_marketplace_proto_goTypes = []any{ (PricingStrategy)(0), // 0: proto.hummingbird.v1.PricingStrategy (ValidityMode)(0), // 1: proto.hummingbird.v1.ValidityMode (*UpdateAssetsRequest)(nil), // 2: proto.hummingbird.v1.UpdateAssetsRequest (*AssetUpdate)(nil), // 3: proto.hummingbird.v1.AssetUpdate - (*Remove)(nil), // 4: proto.hummingbird.v1.Remove - (*UpdateAssetsResponse)(nil), // 5: proto.hummingbird.v1.UpdateAssetsResponse - (*UpdateAssetResult)(nil), // 6: proto.hummingbird.v1.UpdateAssetResult - (*StatisticsRequest)(nil), // 7: proto.hummingbird.v1.StatisticsRequest - (*StatisticsResponse)(nil), // 8: proto.hummingbird.v1.StatisticsResponse - (*StatisticsResponseEntry)(nil), // 9: proto.hummingbird.v1.StatisticsResponseEntry - (*SplitAssetRequest)(nil), // 10: proto.hummingbird.v1.SplitAssetRequest - (*SplitBwOption)(nil), // 11: proto.hummingbird.v1.SplitBwOption - (*SplitTimeOption)(nil), // 12: proto.hummingbird.v1.SplitTimeOption - (*SplitAssetResponse)(nil), // 13: proto.hummingbird.v1.SplitAssetResponse - (*CombineAssetRequest)(nil), // 14: proto.hummingbird.v1.CombineAssetRequest - (*CombineAssetResponse)(nil), // 15: proto.hummingbird.v1.CombineAssetResponse - (*MarketplaceInfoRequest)(nil), // 16: proto.hummingbird.v1.MarketplaceInfoRequest - (*MarketplaceInfoResponse)(nil), // 17: proto.hummingbird.v1.MarketplaceInfoResponse - (*PublishAssetRequest)(nil), // 18: proto.hummingbird.v1.PublishAssetRequest - (*PublishAssetResponse)(nil), // 19: proto.hummingbird.v1.PublishAssetResponse - (*PublisherAsset)(nil), // 20: proto.hummingbird.v1.PublisherAsset - (*SearchAssetsRequest)(nil), // 21: proto.hummingbird.v1.SearchAssetsRequest - (*SearchOwnedAssetsRequest)(nil), // 22: proto.hummingbird.v1.SearchOwnedAssetsRequest - (*SearchListedAssetsRequest)(nil), // 23: proto.hummingbird.v1.SearchListedAssetsRequest - (*SearchAssetsResponse)(nil), // 24: proto.hummingbird.v1.SearchAssetsResponse - (*SearchAsset)(nil), // 25: proto.hummingbird.v1.SearchAsset - (*SearchAssetInterval)(nil), // 26: proto.hummingbird.v1.SearchAssetInterval - (*BuyAssetsRequest)(nil), // 27: proto.hummingbird.v1.BuyAssetsRequest - (*BuyAsset)(nil), // 28: proto.hummingbird.v1.BuyAsset - (*BuyAssetsResponse)(nil), // 29: proto.hummingbird.v1.BuyAssetsResponse - (*BoughtAsset)(nil), // 30: proto.hummingbird.v1.BoughtAsset - (*RedeemAssetRequest)(nil), // 31: proto.hummingbird.v1.RedeemAssetRequest - (*IngressEgressPair)(nil), // 32: proto.hummingbird.v1.IngressEgressPair - (*RedeemAssetResponse)(nil), // 33: proto.hummingbird.v1.RedeemAssetResponse - (*FetchReservationsRequest)(nil), // 34: proto.hummingbird.v1.FetchReservationsRequest - (*FetchReservationsResponse)(nil), // 35: proto.hummingbird.v1.FetchReservationsResponse - (*Reservation)(nil), // 36: proto.hummingbird.v1.Reservation + (*UpdateAsset)(nil), // 4: proto.hummingbird.v1.UpdateAsset + (*Remove)(nil), // 5: proto.hummingbird.v1.Remove + (*UpdateAssetsResponse)(nil), // 6: proto.hummingbird.v1.UpdateAssetsResponse + (*UpdateAssetResult)(nil), // 7: proto.hummingbird.v1.UpdateAssetResult + (*StatisticsRequest)(nil), // 8: proto.hummingbird.v1.StatisticsRequest + (*StatisticsResponse)(nil), // 9: proto.hummingbird.v1.StatisticsResponse + (*StatisticsResponseEntry)(nil), // 10: proto.hummingbird.v1.StatisticsResponseEntry + (*SplitAssetRequest)(nil), // 11: proto.hummingbird.v1.SplitAssetRequest + (*SplitBwOption)(nil), // 12: proto.hummingbird.v1.SplitBwOption + (*SplitTimeOption)(nil), // 13: proto.hummingbird.v1.SplitTimeOption + (*SplitAssetResponse)(nil), // 14: proto.hummingbird.v1.SplitAssetResponse + (*CombineAssetRequest)(nil), // 15: proto.hummingbird.v1.CombineAssetRequest + (*CombineAssetResponse)(nil), // 16: proto.hummingbird.v1.CombineAssetResponse + (*MarketplaceInfoRequest)(nil), // 17: proto.hummingbird.v1.MarketplaceInfoRequest + (*MarketplaceInfoResponse)(nil), // 18: proto.hummingbird.v1.MarketplaceInfoResponse + (*PublishAssetRequest)(nil), // 19: proto.hummingbird.v1.PublishAssetRequest + (*PublishAssetResponse)(nil), // 20: proto.hummingbird.v1.PublishAssetResponse + (*PublisherAsset)(nil), // 21: proto.hummingbird.v1.PublisherAsset + (*SearchAssetsRequest)(nil), // 22: proto.hummingbird.v1.SearchAssetsRequest + (*SearchOwnedAssetsRequest)(nil), // 23: proto.hummingbird.v1.SearchOwnedAssetsRequest + (*SearchListedAssetsRequest)(nil), // 24: proto.hummingbird.v1.SearchListedAssetsRequest + (*SearchAssetsResponse)(nil), // 25: proto.hummingbird.v1.SearchAssetsResponse + (*SearchAsset)(nil), // 26: proto.hummingbird.v1.SearchAsset + (*SearchAssetInterval)(nil), // 27: proto.hummingbird.v1.SearchAssetInterval + (*BuyAssetsRequest)(nil), // 28: proto.hummingbird.v1.BuyAssetsRequest + (*BuyAsset)(nil), // 29: proto.hummingbird.v1.BuyAsset + (*BuyAssetsResponse)(nil), // 30: proto.hummingbird.v1.BuyAssetsResponse + (*BoughtAsset)(nil), // 31: proto.hummingbird.v1.BoughtAsset + (*RedeemAssetRequest)(nil), // 32: proto.hummingbird.v1.RedeemAssetRequest + (*IngressEgressPair)(nil), // 33: proto.hummingbird.v1.IngressEgressPair + (*RedeemAssetResponse)(nil), // 34: proto.hummingbird.v1.RedeemAssetResponse + (*FetchReservationsRequest)(nil), // 35: proto.hummingbird.v1.FetchReservationsRequest + (*FetchReservationsResponse)(nil), // 36: proto.hummingbird.v1.FetchReservationsResponse + (*Reservation)(nil), // 37: proto.hummingbird.v1.Reservation } var file_proto_hummingbird_v1_marketplace_proto_depIdxs = []int32{ 3, // 0: proto.hummingbird.v1.UpdateAssetsRequest.assets:type_name -> proto.hummingbird.v1.AssetUpdate - 20, // 1: proto.hummingbird.v1.AssetUpdate.update:type_name -> proto.hummingbird.v1.PublisherAsset - 4, // 2: proto.hummingbird.v1.AssetUpdate.remove:type_name -> proto.hummingbird.v1.Remove - 6, // 3: proto.hummingbird.v1.UpdateAssetsResponse.result:type_name -> proto.hummingbird.v1.UpdateAssetResult - 9, // 4: proto.hummingbird.v1.StatisticsResponse.statistics:type_name -> proto.hummingbird.v1.StatisticsResponseEntry - 11, // 5: proto.hummingbird.v1.SplitAssetRequest.bw_split:type_name -> proto.hummingbird.v1.SplitBwOption - 12, // 6: proto.hummingbird.v1.SplitAssetRequest.time_split:type_name -> proto.hummingbird.v1.SplitTimeOption + 4, // 1: proto.hummingbird.v1.AssetUpdate.update:type_name -> proto.hummingbird.v1.UpdateAsset + 5, // 2: proto.hummingbird.v1.AssetUpdate.remove:type_name -> proto.hummingbird.v1.Remove + 7, // 3: proto.hummingbird.v1.UpdateAssetsResponse.result:type_name -> proto.hummingbird.v1.UpdateAssetResult + 10, // 4: proto.hummingbird.v1.StatisticsResponse.statistics:type_name -> proto.hummingbird.v1.StatisticsResponseEntry + 12, // 5: proto.hummingbird.v1.SplitAssetRequest.bw_split:type_name -> proto.hummingbird.v1.SplitBwOption + 13, // 6: proto.hummingbird.v1.SplitAssetRequest.time_split:type_name -> proto.hummingbird.v1.SplitTimeOption 0, // 7: proto.hummingbird.v1.MarketplaceInfoResponse.pricing_strategy:type_name -> proto.hummingbird.v1.PricingStrategy - 20, // 8: proto.hummingbird.v1.PublishAssetRequest.asset:type_name -> proto.hummingbird.v1.PublisherAsset - 22, // 9: proto.hummingbird.v1.SearchAssetsRequest.owned:type_name -> proto.hummingbird.v1.SearchOwnedAssetsRequest - 23, // 10: proto.hummingbird.v1.SearchAssetsRequest.listed:type_name -> proto.hummingbird.v1.SearchListedAssetsRequest + 21, // 8: proto.hummingbird.v1.PublishAssetRequest.asset:type_name -> proto.hummingbird.v1.PublisherAsset + 23, // 9: proto.hummingbird.v1.SearchAssetsRequest.owned:type_name -> proto.hummingbird.v1.SearchOwnedAssetsRequest + 24, // 10: proto.hummingbird.v1.SearchAssetsRequest.listed:type_name -> proto.hummingbird.v1.SearchListedAssetsRequest 1, // 11: proto.hummingbird.v1.SearchAssetsRequest.validity_mode:type_name -> proto.hummingbird.v1.ValidityMode - 25, // 12: proto.hummingbird.v1.SearchAssetsResponse.assets:type_name -> proto.hummingbird.v1.SearchAsset - 26, // 13: proto.hummingbird.v1.SearchAsset.intervals:type_name -> proto.hummingbird.v1.SearchAssetInterval - 28, // 14: proto.hummingbird.v1.BuyAssetsRequest.assets:type_name -> proto.hummingbird.v1.BuyAsset - 30, // 15: proto.hummingbird.v1.BuyAssetsResponse.assets:type_name -> proto.hummingbird.v1.BoughtAsset - 32, // 16: proto.hummingbird.v1.RedeemAssetRequest.pair:type_name -> proto.hummingbird.v1.IngressEgressPair - 36, // 17: proto.hummingbird.v1.FetchReservationsResponse.reservations:type_name -> proto.hummingbird.v1.Reservation - 16, // 18: proto.hummingbird.v1.MarketplaceService.Info:input_type -> proto.hummingbird.v1.MarketplaceInfoRequest - 18, // 19: proto.hummingbird.v1.MarketplaceService.PublishAsset:input_type -> proto.hummingbird.v1.PublishAssetRequest - 21, // 20: proto.hummingbird.v1.MarketplaceService.SearchAssets:input_type -> proto.hummingbird.v1.SearchAssetsRequest - 27, // 21: proto.hummingbird.v1.MarketplaceService.BuyAssets:input_type -> proto.hummingbird.v1.BuyAssetsRequest - 31, // 22: proto.hummingbird.v1.MarketplaceService.RedeemAsset:input_type -> proto.hummingbird.v1.RedeemAssetRequest - 34, // 23: proto.hummingbird.v1.MarketplaceService.FetchReservations:input_type -> proto.hummingbird.v1.FetchReservationsRequest - 10, // 24: proto.hummingbird.v1.MarketplaceService.SplitAsset:input_type -> proto.hummingbird.v1.SplitAssetRequest - 14, // 25: proto.hummingbird.v1.MarketplaceService.CombineAssets:input_type -> proto.hummingbird.v1.CombineAssetRequest - 7, // 26: proto.hummingbird.v1.MarketplaceService.Statistics:input_type -> proto.hummingbird.v1.StatisticsRequest + 26, // 12: proto.hummingbird.v1.SearchAssetsResponse.assets:type_name -> proto.hummingbird.v1.SearchAsset + 27, // 13: proto.hummingbird.v1.SearchAsset.intervals:type_name -> proto.hummingbird.v1.SearchAssetInterval + 29, // 14: proto.hummingbird.v1.BuyAssetsRequest.assets:type_name -> proto.hummingbird.v1.BuyAsset + 31, // 15: proto.hummingbird.v1.BuyAssetsResponse.assets:type_name -> proto.hummingbird.v1.BoughtAsset + 33, // 16: proto.hummingbird.v1.RedeemAssetRequest.pair:type_name -> proto.hummingbird.v1.IngressEgressPair + 37, // 17: proto.hummingbird.v1.FetchReservationsResponse.reservations:type_name -> proto.hummingbird.v1.Reservation + 17, // 18: proto.hummingbird.v1.MarketplaceService.Info:input_type -> proto.hummingbird.v1.MarketplaceInfoRequest + 19, // 19: proto.hummingbird.v1.MarketplaceService.PublishAsset:input_type -> proto.hummingbird.v1.PublishAssetRequest + 22, // 20: proto.hummingbird.v1.MarketplaceService.SearchAssets:input_type -> proto.hummingbird.v1.SearchAssetsRequest + 28, // 21: proto.hummingbird.v1.MarketplaceService.BuyAssets:input_type -> proto.hummingbird.v1.BuyAssetsRequest + 32, // 22: proto.hummingbird.v1.MarketplaceService.RedeemAsset:input_type -> proto.hummingbird.v1.RedeemAssetRequest + 35, // 23: proto.hummingbird.v1.MarketplaceService.FetchReservations:input_type -> proto.hummingbird.v1.FetchReservationsRequest + 11, // 24: proto.hummingbird.v1.MarketplaceService.SplitAsset:input_type -> proto.hummingbird.v1.SplitAssetRequest + 15, // 25: proto.hummingbird.v1.MarketplaceService.CombineAssets:input_type -> proto.hummingbird.v1.CombineAssetRequest + 8, // 26: proto.hummingbird.v1.MarketplaceService.Statistics:input_type -> proto.hummingbird.v1.StatisticsRequest 2, // 27: proto.hummingbird.v1.MarketplaceService.UpdateAssets:input_type -> proto.hummingbird.v1.UpdateAssetsRequest - 17, // 28: proto.hummingbird.v1.MarketplaceService.Info:output_type -> proto.hummingbird.v1.MarketplaceInfoResponse - 19, // 29: proto.hummingbird.v1.MarketplaceService.PublishAsset:output_type -> proto.hummingbird.v1.PublishAssetResponse - 24, // 30: proto.hummingbird.v1.MarketplaceService.SearchAssets:output_type -> proto.hummingbird.v1.SearchAssetsResponse - 29, // 31: proto.hummingbird.v1.MarketplaceService.BuyAssets:output_type -> proto.hummingbird.v1.BuyAssetsResponse - 33, // 32: proto.hummingbird.v1.MarketplaceService.RedeemAsset:output_type -> proto.hummingbird.v1.RedeemAssetResponse - 35, // 33: proto.hummingbird.v1.MarketplaceService.FetchReservations:output_type -> proto.hummingbird.v1.FetchReservationsResponse - 13, // 34: proto.hummingbird.v1.MarketplaceService.SplitAsset:output_type -> proto.hummingbird.v1.SplitAssetResponse - 15, // 35: proto.hummingbird.v1.MarketplaceService.CombineAssets:output_type -> proto.hummingbird.v1.CombineAssetResponse - 8, // 36: proto.hummingbird.v1.MarketplaceService.Statistics:output_type -> proto.hummingbird.v1.StatisticsResponse - 5, // 37: proto.hummingbird.v1.MarketplaceService.UpdateAssets:output_type -> proto.hummingbird.v1.UpdateAssetsResponse + 18, // 28: proto.hummingbird.v1.MarketplaceService.Info:output_type -> proto.hummingbird.v1.MarketplaceInfoResponse + 20, // 29: proto.hummingbird.v1.MarketplaceService.PublishAsset:output_type -> proto.hummingbird.v1.PublishAssetResponse + 25, // 30: proto.hummingbird.v1.MarketplaceService.SearchAssets:output_type -> proto.hummingbird.v1.SearchAssetsResponse + 30, // 31: proto.hummingbird.v1.MarketplaceService.BuyAssets:output_type -> proto.hummingbird.v1.BuyAssetsResponse + 34, // 32: proto.hummingbird.v1.MarketplaceService.RedeemAsset:output_type -> proto.hummingbird.v1.RedeemAssetResponse + 36, // 33: proto.hummingbird.v1.MarketplaceService.FetchReservations:output_type -> proto.hummingbird.v1.FetchReservationsResponse + 14, // 34: proto.hummingbird.v1.MarketplaceService.SplitAsset:output_type -> proto.hummingbird.v1.SplitAssetResponse + 16, // 35: proto.hummingbird.v1.MarketplaceService.CombineAssets:output_type -> proto.hummingbird.v1.CombineAssetResponse + 9, // 36: proto.hummingbird.v1.MarketplaceService.Statistics:output_type -> proto.hummingbird.v1.StatisticsResponse + 6, // 37: proto.hummingbird.v1.MarketplaceService.UpdateAssets:output_type -> proto.hummingbird.v1.UpdateAssetsResponse 28, // [28:38] is the sub-list for method output_type 18, // [18:28] is the sub-list for method input_type 18, // [18:18] is the sub-list for extension type_name @@ -2752,35 +2843,33 @@ func file_proto_hummingbird_v1_marketplace_proto_init() { (*AssetUpdate_Update)(nil), (*AssetUpdate_Remove)(nil), } - file_proto_hummingbird_v1_marketplace_proto_msgTypes[4].OneofWrappers = []any{ - (*UpdateAssetResult_NewId)(nil), - (*UpdateAssetResult_Error)(nil), - } + file_proto_hummingbird_v1_marketplace_proto_msgTypes[2].OneofWrappers = []any{} file_proto_hummingbird_v1_marketplace_proto_msgTypes[5].OneofWrappers = []any{} - file_proto_hummingbird_v1_marketplace_proto_msgTypes[8].OneofWrappers = []any{ + file_proto_hummingbird_v1_marketplace_proto_msgTypes[6].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[9].OneofWrappers = []any{ (*SplitAssetRequest_BwSplit)(nil), (*SplitAssetRequest_TimeSplit)(nil), } - file_proto_hummingbird_v1_marketplace_proto_msgTypes[18].OneofWrappers = []any{} - file_proto_hummingbird_v1_marketplace_proto_msgTypes[19].OneofWrappers = []any{ + file_proto_hummingbird_v1_marketplace_proto_msgTypes[19].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[20].OneofWrappers = []any{ (*SearchAssetsRequest_Owned)(nil), (*SearchAssetsRequest_Listed)(nil), } - file_proto_hummingbird_v1_marketplace_proto_msgTypes[20].OneofWrappers = []any{} file_proto_hummingbird_v1_marketplace_proto_msgTypes[21].OneofWrappers = []any{} - file_proto_hummingbird_v1_marketplace_proto_msgTypes[23].OneofWrappers = []any{} - file_proto_hummingbird_v1_marketplace_proto_msgTypes[29].OneofWrappers = []any{ + file_proto_hummingbird_v1_marketplace_proto_msgTypes[22].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[24].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[30].OneofWrappers = []any{ (*RedeemAssetRequest_Pair)(nil), (*RedeemAssetRequest_IfPairAssetId)(nil), } - file_proto_hummingbird_v1_marketplace_proto_msgTypes[32].OneofWrappers = []any{} + file_proto_hummingbird_v1_marketplace_proto_msgTypes[33].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_hummingbird_v1_marketplace_proto_rawDesc), len(file_proto_hummingbird_v1_marketplace_proto_rawDesc)), NumEnums: 2, - NumMessages: 35, + NumMessages: 36, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/hummingbird/v1/marketplace.proto b/proto/hummingbird/v1/marketplace.proto index 3227d8ba9f..c359a84926 100644 --- a/proto/hummingbird/v1/marketplace.proto +++ b/proto/hummingbird/v1/marketplace.proto @@ -37,19 +37,27 @@ message UpdateAssetsRequest { message AssetUpdate { bytes asset_id = 1; oneof operation { - PublisherAsset update = 2; + UpdateAsset update = 2; Remove remove = 3; } } +message UpdateAsset { + optional uint32 bandwidth = 1; + optional uint32 bandwidth_min = 2; + optional uint32 bandwidth_max = 3; + optional int64 starts_at = 4; + optional int64 stops_at = 5; + optional uint32 time_granularity = 6; + optional uint32 time_min_duration = 7; + optional uint32 time_max_duration = 8; + optional uint32 price = 9; +} message Remove {} message UpdateAssetsResponse { repeated UpdateAssetResult result = 1; } message UpdateAssetResult { - oneof result_type { - bytes new_id = 1; - string error = 2; - } + optional string error = 1; } message StatisticsRequest { From 9cc7ce2d596615288b1bab60788230a10fd6195e Mon Sep 17 00:00:00 2001 From: rohrerj <26304001+rohrerj@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:13:31 +0200 Subject: [PATCH 09/14] diagram to visualize asset utilization --- marketplace/db/sqlite.go | 63 +++- marketplace/db/sqlite_test.go | 16 +- marketplace/db/types.go | 8 +- marketplace/service.go | 14 +- marketplace/storage/storage.go | 89 +++-- marketplace/webapp/BUILD.bazel | 2 + marketplace/webapp/static/style.css | 232 +++++++++++++ marketplace/webapp/static/visualize-assets.js | 313 ++++++++++++++++++ marketplace/webapp/templates/asassets.html | 126 +++++++ marketplace/webapp/templates/asbalance.html | 1 + marketplace/webapp/templates/assets.html | 2 +- marketplace/webapp/web.go | 71 ++++ 12 files changed, 881 insertions(+), 56 deletions(-) create mode 100644 marketplace/webapp/static/visualize-assets.js create mode 100644 marketplace/webapp/templates/asassets.html diff --git a/marketplace/db/sqlite.go b/marketplace/db/sqlite.go index cdb43fe09f..40f6c017fe 100644 --- a/marketplace/db/sqlite.go +++ b/marketplace/db/sqlite.go @@ -36,6 +36,8 @@ type Repository interface { SearchOwnedAssets(ctx context.Context, params *OwnedAssetQuery) ([]*DBAsset, error) SearchListedAssets(ctx context.Context, params *ListedAssetQuery) ([]*DBAsset, error) LookupAsset(ctx context.Context, id int64, ia addr.IA) (*DBAsset, error) + LookupAssets(ctx context.Context, ia addr.IA) ([]*DBAsset, error) + LookupAllAssetSegments(ctx context.Context, id int64) ([]*DBAssetSegment, error) FetchReservations(ctx context.Context, params *ReservationQuery) ([]*DBReservation, error) GetUser(ctx context.Context, id int64) (*DBUser, error) GetUserByName(ctx context.Context, name string) (*DBUser, error) @@ -180,7 +182,7 @@ func scanAssetWithAvailableBandwidthAndUsability(row rowScanner) (*DBAsset, erro &asset.TimeGranularity, &asset.TimeMinDuration, &asset.TimeMaxDuration, - &asset.StartAt, + &asset.StartsAt, &asset.StopsAt, &asset.IfIdIngress, &asset.IfIdEgress, @@ -196,7 +198,7 @@ func scanAssetWithAvailableBandwidthAndUsability(row rowScanner) (*DBAsset, erro return nil, err } asset.Bandwidth = availableBW - asset.StartAt = usableFrom + asset.StartsAt = usableFrom asset.StopsAt = usableTo return asset, nil } @@ -218,7 +220,7 @@ func scanAssetWithAvailableBandwidth(row rowScanner) (*DBAsset, error) { &asset.TimeGranularity, &asset.TimeMinDuration, &asset.TimeMaxDuration, - &asset.StartAt, + &asset.StartsAt, &asset.StopsAt, &asset.IfIdIngress, &asset.IfIdEgress, @@ -251,7 +253,7 @@ func scanAsset(row rowScanner) (*DBAsset, error) { &asset.TimeGranularity, &asset.TimeMinDuration, &asset.TimeMaxDuration, - &asset.StartAt, + &asset.StartsAt, &asset.StopsAt, &asset.IfIdIngress, &asset.IfIdEgress, @@ -647,7 +649,7 @@ func (e *executor) RegisterAssetEvent(ctx context.Context, a *DBAsset, eventType inst := `INSERT INTO Asset_Events (event_type, isd_id, as_id, ingress, egress, bandwidth, starts_at, stops_at, price) VALUES(?,?,?,?,?,?,?,?,?)` res, err := e.write.ExecContext(ctx, inst, eventType, a.IA.ISD(), a.IA.AS(), a.IfIdIngress, a.IfIdEgress, a.Bandwidth, - a.StartAt, a.StopsAt, a.Price) + a.StartsAt, a.StopsAt, a.Price) if err != nil { return 0, err } @@ -698,6 +700,47 @@ func (e *executor) LookupAsset(ctx context.Context, id int64, ia addr.IA) (*DBAs return scanAsset(rows) } +func (e *executor) LookupAssets(ctx context.Context, ia addr.IA) ([]*DBAsset, error) { + if e.read == nil { + return nil, serrors.New("No database open") + } + q := `SELECT ` + assetColumnsWithAlias + ` FROM Assets a WHERE a.isd_id = ? AND a.as_id = ? AND a.account_id IS NULL` + rows, err := e.read.QueryContext(ctx, q, ia.ISD(), ia.AS()) + if err != nil { + return nil, err + } + res := []*DBAsset{} + for rows.Next() { + asset, err := scanAsset(rows) + if err != nil { + return nil, err + } + res = append(res, asset) + } + return res, nil +} + +func (e *executor) LookupAllAssetSegments(ctx context.Context, id int64) ([]*DBAssetSegment, error) { + if e.read == nil { + return nil, serrors.New("No database open") + } + q := `SELECT starts_at, stops_at, available FROM Asset_Segment WHERE asset_id = ?` + rows, err := e.read.QueryContext(ctx, q, id) + if err != nil { + return nil, err + } + res := []*DBAssetSegment{} + for rows.Next() { + seg := &DBAssetSegment{} + err = rows.Scan(&seg.StartsAt, &seg.StopsAt, &seg.Available) + if err != nil { + return nil, err + } + res = append(res, seg) + } + return res, nil +} + func (e *executor) SearchListedAssets(ctx context.Context, params *ListedAssetQuery) ([]*DBAsset, error) { if e.read == nil { return nil, serrors.New("No database open") @@ -1019,7 +1062,7 @@ func (e *executor) InsertAsset(ctx context.Context, a *DBAsset) (int64, error) { a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, - a.StartAt, + a.StartsAt, a.StopsAt, a.IfIdIngress, a.IfIdEgress, @@ -1037,7 +1080,7 @@ func (e *executor) InsertAsset(ctx context.Context, a *DBAsset) (int64, error) { a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, - a.StartAt, + a.StartsAt, a.StopsAt, a.IfIdIngress, a.IfIdEgress) @@ -1406,7 +1449,7 @@ func (e *executor) UpdateAsset(ctx context.Context, a *DBAsset) (int64, error) { time_min_duration = ?, time_max_duration = ?, starts_at = ?, stops_at = ? WHERE id = ? AND state = ? AND account_ID IS NULL` res, err := e.write.ExecContext(ctx, inst, a.Bandwidth, a.BandwidthMin, a.BandwidthMax, - a.Price, a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, a.StartAt, + a.Price, a.TimeGranularity, a.TimeMinDuration, a.TimeMaxDuration, a.StartsAt, a.StopsAt, a.ID, AssetStateAvailable) if err != nil { return 0, err @@ -1524,7 +1567,7 @@ func (e *executor) BuyFromAssetSegment( }, IA: asset.IA, Bandwidth: bw, - StartAt: startsAt, + StartsAt: startsAt, StopsAt: stopsAt, BandwidthMin: asset.BandwidthMin, BandwidthMax: asset.BandwidthMax, @@ -1570,7 +1613,7 @@ func (e *executor) BuyAsset( }, IA: dbAsset.IA, Bandwidth: bw, - StartAt: startsAt, + StartsAt: startsAt, StopsAt: stopsAt, BandwidthMin: dbAsset.BandwidthMin, BandwidthMax: dbAsset.BandwidthMax, diff --git a/marketplace/db/sqlite_test.go b/marketplace/db/sqlite_test.go index 23f25c5781..2303cf2a43 100644 --- a/marketplace/db/sqlite_test.go +++ b/marketplace/db/sqlite_test.go @@ -47,7 +47,7 @@ func testAsset() *DBAsset { Price: 5, TimeGranularity: 60, TimeMinDuration: 60, - StartAt: start.Unix(), + StartsAt: start.Unix(), StopsAt: start.Add(time.Hour).Unix(), } } @@ -59,7 +59,7 @@ func insertAsset(ctx context.Context, repo Repository, asset *DBAsset) (int64, e } if !asset.AccountId.Valid { _, err = repo.InsertAssetSegment(ctx, assetId, - asset.StartAt, asset.StopsAt, asset.Bandwidth) + asset.StartsAt, asset.StopsAt, asset.Bandwidth) } return assetId, err } @@ -157,7 +157,7 @@ func TestAssetTransitionAndRowScanners(t *testing.T) { Egress: 2, Bandwidth: 100, EncodedBandwidth: 5, - StartsAt: asset.StartAt, + StartsAt: asset.StartsAt, StopsAt: asset.StopsAt, Key: []byte("key"), } @@ -243,7 +243,7 @@ func TestSearchOwnedAssets(t *testing.T) { require.Equal(t, ownedID, assets[0].ID) // The validity filters still apply to the assets of the account. - startsAt := owned.StartAt + startsAt := owned.StartsAt stopsAt := owned.StopsAt assets, err = backend.SearchOwnedAssets(ctx, &OwnedAssetQuery{ AccountId: &alicesAccountID, @@ -329,7 +329,7 @@ func TestRegisterAssetEvent(t *testing.T) { Bandwidth: 1000, BandwidthMin: 10, BandwidthMax: 1000, - StartAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), + StartsAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), StopsAt: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), TimeGranularity: 10, TimeMinDuration: 10, @@ -348,7 +348,7 @@ func TestRegisterAssetEvent(t *testing.T) { Bandwidth: 1000, BandwidthMin: 10, BandwidthMax: 1000, - StartAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), + StartsAt: time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), StopsAt: time.Date(2027, 1, 2, 0, 0, 0, 0, time.UTC).Unix(), TimeGranularity: 10, TimeMinDuration: 10, @@ -371,11 +371,11 @@ func TestRegisterAssetEvent(t *testing.T) { require.Len(t, buyStats, 1) require.Equal(t, publishAsset.Bandwidth, uint32(publishStats[0].Bandwidth)) require.Equal(t, publishAsset.Price, uint32(publishStats[0].Price)) - require.Equal(t, publishAsset.StartAt, publishStats[0].StartsAt) + require.Equal(t, publishAsset.StartsAt, publishStats[0].StartsAt) require.Equal(t, publishAsset.StopsAt, publishStats[0].StopsAt) require.Equal(t, buyAsset.Bandwidth, uint32(buyStats[0].Bandwidth)) require.Equal(t, buyAsset.Price, uint32(buyStats[0].Price)) - require.Equal(t, buyAsset.StartAt, buyStats[0].StartsAt) + require.Equal(t, buyAsset.StartsAt, buyStats[0].StartsAt) require.Equal(t, buyAsset.StopsAt, buyStats[0].StopsAt) } diff --git a/marketplace/db/types.go b/marketplace/db/types.go index 6600dc5a84..f8e3c03aae 100644 --- a/marketplace/db/types.go +++ b/marketplace/db/types.go @@ -212,7 +212,7 @@ type DBAsset struct { // the maximum bandwidth allowed at redemption BandwidthMax uint32 // inclusive start time of the asset - StartAt int64 + StartsAt int64 // exclusive end time of the asset StopsAt int64 // price per kbps per second of the asset @@ -229,6 +229,12 @@ type DBAsset struct { IfIdEgress sql.NullInt32 } +type DBAssetSegment struct { + StartsAt int64 `json:"starts_at"` + StopsAt int64 `json:"stops_at"` + Available int64 `json:"available"` +} + type DBUser struct { // marketplace-wide unique user ID ID int64 diff --git a/marketplace/service.go b/marketplace/service.go index 396e203f7a..bc796ef15a 100644 --- a/marketplace/service.go +++ b/marketplace/service.go @@ -329,7 +329,7 @@ func (s *Service) PublishAsset(ctx context.Context, req *connect.Request[humming Bandwidth: req.Msg.Asset.Bandwidth, BandwidthMin: req.Msg.Asset.BandwidthMin, BandwidthMax: req.Msg.Asset.BandwidthMax, - StartAt: req.Msg.Asset.StartsAt, + StartsAt: req.Msg.Asset.StartsAt, StopsAt: req.Msg.Asset.StopsAt, Price: req.Msg.Asset.Price, TimeGranularity: req.Msg.Asset.TimeGranularity, @@ -341,7 +341,7 @@ func (s *Service) PublishAsset(ctx context.Context, req *connect.Request[humming if time.Unix(dbAsset.StopsAt, 0).UTC().After(time.Now().Add(time.Duration(s.info.AssetValidityMax) * time.Second).UTC()) { return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("validity outside allowed range")) } - if dbAsset.StopsAt <= dbAsset.StartAt { + if dbAsset.StopsAt <= dbAsset.StartsAt { return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("stopsAt must come after startsAt")) } if req.Msg.Asset.IfIdIngress != nil { @@ -412,7 +412,7 @@ func (s *Service) RedeemAsset( bw = pairAsset.Bandwidth ingressID = uint32(pairAsset.IfIdIngress.Int32) egressID = uint32(pairAsset.IfIdEgress.Int32) - startsAt = pairAsset.StartAt + startsAt = pairAsset.StartsAt stopsAt = pairAsset.StopsAt ia = pairAsset.IA } else if len(assets) == 2 { @@ -421,7 +421,7 @@ func (s *Service) RedeemAsset( egressAsset := assets[1] if ingressAsset.IA != egressAsset.IA || ingressAsset.Bandwidth != egressAsset.Bandwidth || - ingressAsset.StartAt != egressAsset.StartAt || + ingressAsset.StartsAt != egressAsset.StartsAt || ingressAsset.StopsAt != egressAsset.StopsAt || !ingressAsset.IfIdIngress.Valid || !egressAsset.IfIdEgress.Valid { @@ -431,7 +431,7 @@ func (s *Service) RedeemAsset( ingressID = uint32(ingressAsset.IfIdIngress.Int32) egressID = uint32(egressAsset.IfIdEgress.Int32) ia = ingressAsset.IA - startsAt = ingressAsset.StartAt + startsAt = ingressAsset.StartsAt stopsAt = ingressAsset.StopsAt } else { return nil, connect.NewError(connect.CodeInvalidArgument, serrors.New("invalid assets")) @@ -686,7 +686,7 @@ func (s *Service) SearchAssets(ctx context.Context, req *connect.Request[humming if found { mapAsset.Intervals = append(mapAsset.Intervals, &hummingbird.SearchAssetInterval{ Bandwidth: asset.Bandwidth, - StartsAt: asset.StartAt, + StartsAt: asset.StartsAt, StopsAt: asset.StopsAt, }) } else { @@ -702,7 +702,7 @@ func (s *Service) SearchAssets(ctx context.Context, req *connect.Request[humming Intervals: []*hummingbird.SearchAssetInterval{ &hummingbird.SearchAssetInterval{ Bandwidth: asset.Bandwidth, - StartsAt: asset.StartAt, + StartsAt: asset.StartsAt, StopsAt: asset.StopsAt, }, }, diff --git a/marketplace/storage/storage.go b/marketplace/storage/storage.go index 9fe258bc0d..752a5c36d5 100644 --- a/marketplace/storage/storage.go +++ b/marketplace/storage/storage.go @@ -104,7 +104,7 @@ func (s *MarketplaceStorage) PublishAsset( if err != nil { return err } - _, err = tx.InsertAssetSegment(ctx, assetId, a.StartAt, a.StopsAt, a.Bandwidth) + _, err = tx.InsertAssetSegment(ctx, assetId, a.StartsAt, a.StopsAt, a.Bandwidth) if err != nil { return err } @@ -131,7 +131,7 @@ func (s *MarketplaceStorage) UpdateListedAsset( } // check for illegal modifications if (a.Bandwidth != nil && *a.Bandwidth < dbAsset.Bandwidth) || - (a.StartsAt != nil && *a.StartsAt > dbAsset.StartAt) || + (a.StartsAt != nil && *a.StartsAt > dbAsset.StartsAt) || (a.StopsAt != nil && *a.StopsAt < dbAsset.StopsAt) { return serrors.New("bandwidth and validity period cannot be decreased") } @@ -141,14 +141,14 @@ func (s *MarketplaceStorage) UpdateListedAsset( } dbAsset.Bandwidth = *a.Bandwidth } - if a.StartsAt != nil && *a.StartsAt < dbAsset.StartAt { - if _, err = tx.InsertAssetSegment(ctx, id, *a.StartsAt, dbAsset.StartAt, dbAsset.Bandwidth); err != nil { + if a.StartsAt != nil && *a.StartsAt < dbAsset.StartsAt { + if _, err = tx.InsertAssetSegment(ctx, id, *a.StartsAt, dbAsset.StartsAt, dbAsset.Bandwidth); err != nil { return err } - if err = tx.MergeAssetAt(ctx, id, dbAsset.StartAt); err != nil { + if err = tx.MergeAssetAt(ctx, id, dbAsset.StartsAt); err != nil { return err } - dbAsset.StartAt = *a.StartsAt + dbAsset.StartsAt = *a.StartsAt } if a.StopsAt != nil && *a.StopsAt > dbAsset.StopsAt { if _, err = tx.InsertAssetSegment(ctx, id, dbAsset.StopsAt, *a.StopsAt, dbAsset.Bandwidth); err != nil { @@ -498,7 +498,7 @@ func (s *MarketplaceStorage) CombineAssets( ingress := assets[0].IfIdIngress egress := assets[0].IfIdEgress combinedStop := assets[0].StopsAt - useTimeAxis := assets[0].StopsAt == assets[1].StartAt + useTimeAxis := assets[0].StopsAt == assets[1].StartsAt combinedMinBW := assets[0].BandwidthMin combinedMaxBW := assets[0].BandwidthMax combinedTimeMinDuration := assets[0].TimeMinDuration @@ -525,12 +525,12 @@ func (s *MarketplaceStorage) CombineAssets( if assets[0].Bandwidth != asset.Bandwidth { return serrors.New("assets cannot be combined") } - if combinedStop != asset.StartAt { + if combinedStop != asset.StartsAt { return serrors.New("assets cannot be combined") } combinedStop = asset.StopsAt } else { - if asset.StartAt != assets[0].StartAt || asset.StopsAt != assets[0].StopsAt { + if asset.StartsAt != assets[0].StartsAt || asset.StopsAt != assets[0].StopsAt { return serrors.New("assets cannot be combined") } combinedBandwidth += asset.Bandwidth @@ -550,7 +550,7 @@ func (s *MarketplaceStorage) CombineAssets( TimeMaxDuration: combinedTimeMaxDuration, TimeGranularity: combinedTimeGranularity, Bandwidth: combinedBandwidth, - StartAt: assets[0].StartAt, + StartsAt: assets[0].StartsAt, StopsAt: combinedStop, } for _, assetId := range assetIds { @@ -587,7 +587,7 @@ func (s *MarketplaceStorage) SplitAsset( } var splitSegments []AssetSegment baseSegment := AssetSegment{ - StartsAt: dbAsset.StartAt, + StartsAt: dbAsset.StartsAt, StopsAt: dbAsset.StopsAt, Bandwidth: dbAsset.Bandwidth, } @@ -649,7 +649,7 @@ func (s *MarketplaceStorage) SplitAsset( IfIdIngress: dbAsset.IfIdIngress, IfIdEgress: dbAsset.IfIdEgress, Bandwidth: segment.Bandwidth, - StartAt: segment.StartsAt, + StartsAt: segment.StartsAt, StopsAt: segment.StopsAt, } splitAssetId, err := tx.InsertAsset(ctx, asset) @@ -704,7 +704,7 @@ func (s *MarketplaceStorage) BuyAssets( err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { calcCost := func(a *marketplacedb.DBAsset) (int64, bool) { - totalAssetPrice, ok := multiplyInts(int64(a.StopsAt-a.StartAt), + totalAssetPrice, ok := multiplyInts(int64(a.StopsAt-a.StartsAt), int64(a.Bandwidth), int64(a.Price)) if !ok { return 0, false @@ -718,43 +718,51 @@ func (s *MarketplaceStorage) BuyAssets( startsAt int64, stopsAt int64, bw uint32, - ) (*marketplacedb.DBAsset, error) { + ) (*marketplacedb.DBAsset, int64, error) { asset, err := tx.CheckAssetAvailability(ctx, assetId, startsAt, stopsAt, bw) if err != nil { - return nil, err + return nil, 0, err } if err = tx.SplitAssetAt(ctx, assetId, startsAt); err != nil { - return nil, err + return nil, 0, err } if err = tx.SplitAssetAt(ctx, assetId, stopsAt); err != nil { - return nil, err + return nil, 0, err } userAsset, err := tx.BuyAsset(ctx, startsAt, stopsAt, bw, accountID, asset) if err != nil { - return nil, err + return nil, 0, err } if err = tx.MergeAssetAt(ctx, assetId, startsAt); err != nil { - return nil, err + return nil, 0, err } if err = tx.MergeAssetAt(ctx, assetId, stopsAt); err != nil { - return nil, err + return nil, 0, err } - return userAsset, nil + _, err = tx.RegisterAssetEvent(ctx, userAsset, marketplacedb.AssetBought) + if err != nil { + return nil, 0, err + } + cost, ok := calcCost(userAsset) + if !ok { + return nil, 0, serrors.New("asset parameters would lead to integer overflow") + } + _, err = tx.UpdateASMoney(ctx, asset.IA, cost) + if err != nil { + return nil, 0, err + } + return userAsset, cost, nil } for _, asset := range assets { assetId, err := DatabaseAssetID(asset.AssetId) if err != nil { return err } - newAsset, err := buyAsset(assetId, asset.StartsAtExactly, asset.StopsAtExactly, asset.BandwidthExact) + newAsset, cost, err := buyAsset(assetId, asset.StartsAtExactly, asset.StopsAtExactly, asset.BandwidthExact) if err != nil { return err } - cost, ok := calcCost(newAsset) - if !ok { - return serrors.New("asset parameters would lead to integer overflow") - } - costAcc, ok = addInt64(costAcc, cost) + costAcc, ok := addInt64(costAcc, cost) if !ok { return serrors.New("asset parameters would lead to integer overflow") } @@ -812,7 +820,7 @@ func (s *MarketplaceStorage) UndoRedemption( func validateAsset( a *marketplacedb.DBAsset, ) error { - duration := a.StopsAt - a.StartAt + duration := a.StopsAt - a.StartsAt if a.Bandwidth == 0 { return serrors.New("bandwidth is 0") } @@ -824,7 +832,7 @@ func validateAsset( func validateAssetForRedemption( a *marketplacedb.DBAsset, ) error { - duration := a.StopsAt - a.StartAt + duration := a.StopsAt - a.StartsAt if err := validateAsset(a); err != nil { return err } @@ -1011,3 +1019,26 @@ func (s *MarketplaceStorage) AssignReservation( } return 1, nil } + +func (s *MarketplaceStorage) LookupAssets(ctx context.Context, ia addr.IA) ([]*marketplacedb.DBAsset, error) { + return s.db.LookupAssets(ctx, ia) +} + +func (s *MarketplaceStorage) LookupAllAssetSegments(ctx context.Context, id int64, ia addr.IA) ([]*marketplacedb.DBAssetSegment, error) { + var segments []*marketplacedb.DBAssetSegment + err := s.db.WithTx(ctx, func(tx marketplacedb.Repository) error { + _, err := tx.LookupAsset(ctx, id, ia) + if err != nil { + return err + } + segments, err = tx.LookupAllAssetSegments(ctx, id) + if err != nil { + return err + } + return nil + }) + if err != nil { + return nil, err + } + return segments, nil +} diff --git a/marketplace/webapp/BUILD.bazel b/marketplace/webapp/BUILD.bazel index ff6b803cd1..024eac8c7f 100644 --- a/marketplace/webapp/BUILD.bazel +++ b/marketplace/webapp/BUILD.bazel @@ -6,7 +6,9 @@ go_library( embedsrcs = [ "static/script.js", "static/style.css", + "static/visualize-assets.js", "templates/account.html", + "templates/asassets.html", "templates/asbalance.html", "templates/aslogin.html", "templates/assets.html", diff --git a/marketplace/webapp/static/style.css b/marketplace/webapp/static/style.css index 6919584167..88797e0f0f 100644 --- a/marketplace/webapp/static/style.css +++ b/marketplace/webapp/static/style.css @@ -286,4 +286,236 @@ select:focus { select option { padding: 8px; +} + + +/* viz section */ +.util { + --util-low: #5fa58f; + --util-mid: #d4a73c; + --util-high: #de7f3f; + --util-full: #cf4a3f; + --util-plot-h: 240px; + + --util-line: color-mix(in srgb, currentColor 18%, transparent); + --util-hatch: color-mix(in srgb, currentColor 25%, transparent); + --util-muted: color-mix(in srgb, currentColor 60%, transparent); +} + +.util *, +.util *::before, +.util *::after { + box-sizing: border-box; +} + +/* bar level colours (shared by bars and legend swatches) */ +.util-low { + --c: var(--util-low); +} + +.util-mid { + --c: var(--util-mid); +} + +.util-high { + --c: var(--util-high); +} + +.util-full { + --c: var(--util-full); +} + +/* header, controls, stats */ +.util-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1em; + margin-bottom: 1em; +} + +.util-head h2 { + margin: 0; +} + +.util-error { + margin: 0 0 .75em; + color: var(--util-full); +} + +.util-controls { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + gap: .75em 1em; + margin-bottom: .5em; +} + +.util-controls label { + display: grid; + gap: .2em; + font-size: .8em; + color: var(--util-muted); +} + +.util-buttons { + display: flex; + gap: .35em; +} + +.util-hint { + margin: 0 0 1em; + font-size: .8em; + color: var(--util-muted); +} + +.util-stats { + display: flex; + flex-wrap: wrap; + gap: .25em 1.5em; + margin-bottom: .75em; + font-size: .85em; + color: var(--util-muted); +} + +.util-stats b { + color: inherit; + font-weight: 600; + font-variant-numeric: tabular-nums; +} + +/* chart: y-axis gutter + plot, x-axis underneath */ +.util-chart { + display: grid; + grid-template-columns: 4em 1fr; +} + +.util-y { + position: relative; + height: var(--util-plot-h); +} + +.util-y span { + position: absolute; + right: .5em; + transform: translateY(50%); + font-size: .72em; + color: var(--util-muted); + font-variant-numeric: tabular-nums; +} + +.util-y span.util-top { + transform: none; +} + +.util-plot { + position: relative; + height: var(--util-plot-h); + cursor: crosshair; + user-select: none; + touch-action: none; + border-left: 1px solid var(--util-line); + border-bottom: 1px solid var(--util-line); +} + +.util-gap { + position: absolute; + top: 0; + bottom: 0; + background: repeating-linear-gradient(135deg, transparent 0 5px, var(--util-hatch) 5px 6px); +} + +/* one slot = full-height hover column with the bar drawn inside it */ +.util-seg { + position: absolute; + top: 0; + bottom: 0; + min-width: 1px; +} + +.util-seg::before { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: calc(var(--used) * 1%); + background: var(--c); + border-top: 2px solid color-mix(in srgb, var(--c) 70%, #000); +} + +.util-seg:hover { + background: color-mix(in srgb, currentColor 8%, transparent); +} + +.util-hline { + position: absolute; + left: 0; + right: 0; + border-top: 1px dashed var(--util-line); + pointer-events: none; +} + +.util-vline { + position: absolute; + top: 0; + bottom: 0; + border-left: 1px dashed var(--util-line); + pointer-events: none; + opacity: .7; +} + +.util-brush { + position: absolute; + top: 0; + bottom: 0; + pointer-events: none; + background: color-mix(in srgb, currentColor 12%, transparent); + border-left: 1px solid color-mix(in srgb, currentColor 50%, transparent); + border-right: 1px solid color-mix(in srgb, currentColor 50%, transparent); +} + +.util-x { + position: relative; + height: 1.8em; + grid-column: 2; +} + +.util-x span { + position: absolute; + top: .35em; + transform: translateX(-50%); + font-size: .72em; + color: var(--util-muted); + white-space: nowrap; + font-variant-numeric: tabular-nums; +} + +.util-legend { + display: flex; + flex-wrap: wrap; + gap: 1em; + margin-top: .5em; + font-size: .78em; + color: var(--util-muted); +} + +.util-legend i { + display: inline-block; + width: .8em; + height: .8em; + margin-right: .35em; + border-radius: 2px; + vertical-align: -.1em; + background: var(--c); +} + +.util-legend i.util-nodata { + background: repeating-linear-gradient(135deg, transparent 0 3px, var(--util-hatch) 3px 4px); + border: 1px solid var(--util-hatch); +} + +/* optional: highlight for the asset row whose chart is open (class set by the JS) */ +tr.util-active { + background: color-mix(in srgb, currentColor 8%, transparent); } \ No newline at end of file diff --git a/marketplace/webapp/static/visualize-assets.js b/marketplace/webapp/static/visualize-assets.js new file mode 100644 index 0000000000..944e53e8c1 --- /dev/null +++ b/marketplace/webapp/static/visualize-assets.js @@ -0,0 +1,313 @@ +const Utilization = (() => { + 'use strict'; + + const $ = (id) => document.getElementById(id); + + const MIN_SPAN = 1000; // smallest zoom: 1 second + const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + + const cache = new Map(); // asset id -> parsed asset + let asset = null; // currently shown asset (null until the first load) + let view = { from: 0, to: 0 }; + + // ---------- formatting ---------- + + const pad = (n) => String(n).padStart(2, '0'); + + function toInput(ms) { + const d = new Date(ms); + return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + + 'T' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds()); + } + const fmtFull = (ms) => toInput(ms).replace('T', ' '); + const fmtNum = (n) => n.toLocaleString(undefined, { maximumFractionDigits: 1 }); + const level = (pct) => (pct >= 95 ? 'full' : pct >= 75 ? 'high' : pct >= 40 ? 'mid' : 'low'); + const span = () => view.to - view.from; + + // ---------- loading ---------- + + // The page already knows id, bandwidth, start and end (data-* attributes on + // the button). Only the slots are fetched: POST /slots?id= -> [ {starts_at, stops_at, available}, ... ] + // All timestamps from the server are unix seconds; internally everything is milliseconds. + async function load(data) { + if (cache.has(data.id)) return cache.get(data.id); + const res = await fetch('/asassets/data?id=' + encodeURIComponent(data.id), { method: 'POST' }); + if (!res.ok) throw new Error('Could not load utilization (' + res.status + ')'); + const rawSlots = await res.json(); + const a = { + id: data.id, + bandwidth: Number(data.bandwidth), + start: Number(data.start) * 1000, // unix seconds -> ms for JS Date + end: Number(data.end) * 1000, + slots: rawSlots + .map((s) => ({ s: s.starts_at * 1000, e: s.stops_at * 1000, avail: s.available })) + .sort((p, q) => p.s - q.s), + }; + cache.set(data.id, a); + return a; + } + + function showError(msg) { + const err = $('viz-error'); + err.textContent = msg; + err.hidden = !msg; + } + + function markActiveRow(id) { + document.querySelectorAll('tr[data-id]').forEach((tr) => { + tr.classList.toggle('util-active', tr.dataset.id === id); + }); + } + + // ---------- public actions (called from inline handlers) ---------- + + // onclick on the row button: loads the data, then shows the hidden section. + async function visualize(btn) { + const label = btn.textContent; + btn.disabled = true; + btn.textContent = 'Loading\u2026'; + try { + const a = await load(btn.dataset); + asset = a; + $('viz-title').textContent = "Asset ID " + a.id; + $('stat-bw').textContent = fmtNum(a.bandwidth); + showError(''); + view = { from: a.start, to: a.end }; + $('viz').hidden = false; + markActiveRow(a.id); + render(); + $('viz').scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } catch (err) { + $('viz').hidden = false; + showError(err.message); + } finally { + btn.disabled = false; + btn.textContent = label; + } + } + + function hide() { + $('viz').hidden = true; + markActiveRow(null); + } + + function setView(from, to) { + if (!asset) return; + const sp = Math.max(to - from, MIN_SPAN); + if (sp >= asset.end - asset.start) { + view = { from: asset.start, to: asset.end }; + } else { + if (from < asset.start) from = asset.start; + if (from + sp > asset.end) from = asset.end - sp; + view = { from, to: from + sp }; + } + render(); + } + + function apply() { + if (!asset) return; + const f = new Date($('from').value).getTime(); + const t = new Date($('to').value).getTime(); + if (Number.isNaN(f) || Number.isNaN(t) || t <= f) { + showError('Enter a valid range: "To" must be after "From".'); + return; + } + showError(''); + setView(f, t); + } + + function zoomIn() { + if (!asset) return; + const c = (view.from + view.to) / 2; + const h = span() / 4; + setView(c - h, c + h); + } + + function zoomOut() { + if (!asset) return; + const c = (view.from + view.to) / 2; + const h = span(); + setView(c - h, c + h); + } + + function panLeft() { + if (asset) setView(view.from - span() / 2, view.to - span() / 2); + } + + function panRight() { + if (asset) setView(view.from + span() / 2, view.to + span() / 2); + } + + function reset() { + if (asset) setView(asset.start, asset.end); + } + + // ---------- drag-to-zoom (pointer handlers on the plot) ---------- + + let dragX0 = null; + let brushEl = null; + + function localX(ev) { + const rect = ev.currentTarget.getBoundingClientRect(); + return { x: Math.min(Math.max(ev.clientX - rect.left, 0), rect.width), width: rect.width }; + } + + function brushStart(ev) { + if (!asset || ev.button !== 0) return; + dragX0 = localX(ev).x; + brushEl = document.createElement('div'); + brushEl.className = 'util-brush'; + brushEl.style.left = dragX0 + 'px'; + brushEl.style.width = '0px'; + ev.currentTarget.appendChild(brushEl); + ev.currentTarget.setPointerCapture(ev.pointerId); + } + + function brushMove(ev) { + if (dragX0 === null) return; + const { x } = localX(ev); + brushEl.style.left = Math.min(x, dragX0) + 'px'; + brushEl.style.width = Math.abs(x - dragX0) + 'px'; + } + + function brushEnd(ev) { + if (dragX0 === null) return; + const { x, width } = localX(ev); + const x0 = dragX0; + brushCancel(); + if (Math.abs(x - x0) > 5) { + const toTime = (px) => view.from + (px / width) * span(); + setView(toTime(Math.min(x, x0)), toTime(Math.max(x, x0))); + } + } + + function brushCancel() { + dragX0 = null; + if (brushEl) { + brushEl.remove(); + brushEl = null; + } + } + + // ---------- axis ticks ---------- + + const S = 1000, M = 60 * S, H = 60 * M, D = 24 * H; + const STEPS = [S, 2 * S, 5 * S, 10 * S, 15 * S, 30 * S, M, 2 * M, 5 * M, 10 * M, 15 * M, 30 * M, + H, 2 * H, 3 * H, 6 * H, 12 * H, D, 2 * D, 7 * D, 14 * D, 30 * D, 90 * D, 365 * D]; + + function makeTicks(from, to) { + const sp = to - from; + const step = STEPS.find((s) => sp / s <= 8) || STEPS[STEPS.length - 1]; + const off = -new Date(from).getTimezoneOffset() * M; // align ticks to local time + const ticks = []; + for (let t = Math.ceil((from + off) / step) * step - off; t <= to; t += step) ticks.push(t); + + const label = (t) => { + const d = new Date(t); + const date = d.getDate() + ' ' + MONTHS[d.getMonth()]; + const hm = pad(d.getHours()) + ':' + pad(d.getMinutes()); + if (step >= D) return date; + const time = step < M ? hm + ':' + pad(d.getSeconds()) : hm; + return sp >= D ? date + ' ' + time : time; + }; + return ticks.map((t) => ({ t, text: label(t) })); + } + + // ---------- rendering ---------- + + function render() { + const { from, to } = view; + const sp = to - from; + const bw = asset.bandwidth; + const pos = (t) => ((t - from) / sp) * 100; + + const plot = $('plot'); + const xAxis = $('x'); + const yAxis = $('y'); + + $('from').value = toInput(from); + $('to').value = toInput(to); + $('from').min = $('to').min = toInput(asset.start); + $('from').max = $('to').max = toInput(asset.end); + + plot.replaceChildren(); + xAxis.replaceChildren(); + yAxis.replaceChildren(); + + // y axis + horizontal grid lines (0 .. bandwidth) + [0, 25, 50, 75, 100].forEach((p) => { + const lbl = document.createElement('span'); + lbl.textContent = fmtNum((bw * p) / 100); + lbl.style.bottom = p + '%'; + if (p === 100) lbl.classList.add('util-top'); + yAxis.appendChild(lbl); + if (p > 0) { + const line = document.createElement('div'); + line.className = 'util-hline'; + line.style.bottom = p + '%'; + plot.appendChild(line); + } + }); + + // x axis + vertical grid lines + makeTicks(from, to).forEach(({ t, text }) => { + const lbl = document.createElement('span'); + lbl.textContent = text; + lbl.style.left = pos(t) + '%'; + xAxis.appendChild(lbl); + const line = document.createElement('div'); + line.className = 'util-vline'; + line.style.left = pos(t) + '%'; + plot.appendChild(line); + }); + + // bars: only slots intersecting the view, clipped to it + const frag = document.createDocumentFragment(); + const addGap = (a, b) => { + const gap = document.createElement('div'); + gap.className = 'util-gap'; + gap.style.left = pos(a) + '%'; + gap.style.width = (pos(b) - pos(a)) + '%'; + gap.title = 'No data\n' + fmtFull(a) + ' \u2013 ' + fmtFull(b); + frag.appendChild(gap); + }; + let cursor = from; // end of the data seen so far; anything after it is a gap + let weighted = 0, covered = 0, peak = 0, n = 0; + for (const s of asset.slots) { + if (s.e <= from) continue; + if (s.s >= to) break; // slots are sorted by start + const a = Math.max(s.s, from); + const b = Math.min(s.e, to); + if (a > cursor) addGap(cursor, a); + cursor = Math.max(cursor, b); + const sold = Math.min(Math.max(bw - s.avail, 0), bw); + const pct = (sold / bw) * 100; + weighted += pct * (b - a); + covered += b - a; + peak = Math.max(peak, pct); + n++; + + const seg = document.createElement('div'); + seg.className = 'util-seg util-' + level(pct); + seg.style.left = pos(a) + '%'; + seg.style.width = (pos(b) - pos(a)) + '%'; + seg.style.setProperty('--used', pct.toFixed(2)); + seg.title = fmtFull(s.s) + ' \u2013 ' + fmtFull(s.e) + + '\nSold: ' + fmtNum(sold) + ' / ' + fmtNum(bw) + ' (' + pct.toFixed(0) + '%)' + + '\nAvailable: ' + fmtNum(s.avail); + frag.appendChild(seg); + } + if (cursor < to) addGap(cursor, to); + // bars go first in the DOM so the grid lines draw on top of them + plot.insertBefore(frag, plot.firstChild); + + $('stat-avg').textContent = covered ? (weighted / covered).toFixed(0) + '%' : '\u2013'; + $('stat-peak').textContent = n ? peak.toFixed(0) + '%' : '\u2013'; + $('stat-count').textContent = String(n); + } + + return { + visualize, hide, apply, zoomIn, zoomOut, panLeft, panRight, reset, + brushStart, brushMove, brushEnd, brushCancel + }; +})(); diff --git a/marketplace/webapp/templates/asassets.html b/marketplace/webapp/templates/asassets.html new file mode 100644 index 0000000000..c88f0cd03c --- /dev/null +++ b/marketplace/webapp/templates/asassets.html @@ -0,0 +1,126 @@ + + + + + + View Assets + + + + + + +
+

Assets

+

{{.Error}}

+
+

Assets

+ + + + + + + + + + + + + + {{range $asset := .Assets}} + + + + + + + + + + {{end}} + +
IDIngressEgressStarts atStops atBandwidth
{{.ID}} + {{with .IfIdIngress}} + {{.Value}} + {{end}} + + {{with .IfIdEgress}} + {{.Value}} + {{end}} + {{.StartsAt}}{{.StopsAt}}{{.Bandwidth}} +
+ +
+
+
+ + +
+ + + \ No newline at end of file diff --git a/marketplace/webapp/templates/asbalance.html b/marketplace/webapp/templates/asbalance.html index 6f8cecc438..770eb0d663 100644 --- a/marketplace/webapp/templates/asbalance.html +++ b/marketplace/webapp/templates/asbalance.html @@ -12,6 +12,7 @@