package memdb import ( "bytes" "colinear/x/colinearcore/types" "encoding/gob" "errors" "log" "math/big" badger "github.com/dgraph-io/badger/v3" ) type auctionDB struct { db *badger.DB } var AuctionDB auctionDB // Mount Db & initialize encoder/decoder func (b *auctionDB) Mount() { db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true)) if err != nil { // must force crash here, since db is absolutely required log.Fatalf("Failed to mount in-memory db: %s", err) } b.db = db // initialize encode/decode stuff gob.Register([]types.Bid{}) } // ----------------- // * BID FUNCTIONS * // ----------------- // Add a bid to the bid list under specified auction key. func (b *auctionDB) AddBid(auctionId string, bid *types.Bid) error { k := []byte(auctionId + "_bids") err := b.db.Update(func(txn *badger.Txn) error { var bids []*types.Bid bidsOld, err := txn.Get(k) if errors.Is(err, badger.ErrKeyNotFound) { // key not found -> just create a new Bid array bids = []*types.Bid{} } else { if err != nil { return err } // key found -> decode contents to bids bidsOld.Value(func(val []byte) error { dec := gob.NewDecoder(bytes.NewReader(val)) if err := dec.Decode(&bids); err != nil { return err } return nil }) } // append bid bids = append(bids, bid) // encode new list buf := bytes.NewBuffer(nil) enc := gob.NewEncoder(buf) if err := enc.Encode(&bids); err != nil { return err } // set under auction key in db if err := txn.Set(k, buf.Bytes()); err != nil { return err } return nil }) return err } // Get the highest bid in the list under specified auction key. func (b *auctionDB) GetLowestBid(auctionId string) (*types.Bid, error) { k := []byte(auctionId + "_bids") var bid *types.Bid err := b.db.View(func(txn *badger.Txn) error { bidData, err := txn.Get(k) if err != nil { if !errors.Is(err, badger.ErrKeyNotFound) { return err } else { return nil } } err = bidData.Value(func(val []byte) error { var bids []*types.Bid dec := gob.NewDecoder(bytes.NewReader(val)) if err := dec.Decode(&bids); err != nil { return err } if bids == nil { return nil } minAmt := big.NewInt(0) refAmt := big.NewInt(0) for _, currBid := range bids { if bid == nil { bid = currBid minAmt.SetString(currBid.Amount, 10) } else { // set ref amt. refAmt.SetString(currBid.Amount, 10) // if ref amt is less, then we have a new min if refAmt.Cmp(minAmt) == -1 { bid = currBid } } } return nil }) return err }) return bid, err } func (b *auctionDB) GetBids(auctionId string) ([]*types.Bid, error) { k := []byte(auctionId + "_bids") var bids []*types.Bid err := b.db.View(func(txn *badger.Txn) error { res, err := txn.Get(k) if err != nil { return err } else { err := res.Value(func(val []byte) error { dec := gob.NewDecoder(bytes.NewReader(val)) err := dec.Decode(&bids) return err }) return err } }) return bids, err } func (b *auctionDB) ClearAuctionBids(auctionId string) error { k := []byte(auctionId + "_bids") err := b.db.Update(func(txn *badger.Txn) error { return txn.Delete(k) }) return err } // Iterate over all auction bid-list keys in memory and retrieve bids from each. VIEW-ONLY. func (b *auctionDB) ForEachAuctionBidList(viewFunc func(string) error) error { err := b.db.View(func(txn *badger.Txn) error { opts := badger.DefaultIteratorOptions // can customize options down here if we want iter := txn.NewIterator(opts) defer iter.Close() for iter.Rewind(); iter.Valid(); iter.Next() { item := iter.Item() key := string(item.Key()) // require that this be an auction bids key if len(key) > 5 && key[len(key)-5:] == "_bids" { auctionId := key[:len(key)-5] err := viewFunc(auctionId) if err != nil { return err } } } return nil }) return err } // ------------------------------- // * VERIFIED PROVIDER FUNCTIONS * // ------------------------------- func (b *auctionDB) SetVerifiedProviders(auctionId string, providers []string) error { k := []byte(auctionId + "_vp") if len(providers) == 0 { return errors.New("must include at least one provider") } buf := bytes.NewBuffer(nil) enc := gob.NewEncoder(buf) if err := enc.Encode(&providers); err != nil { return err } err := b.db.Update(func(txn *badger.Txn) error { return txn.Set(k, buf.Bytes()) }) return err } func (b *auctionDB) GetVerifiedProviders(auctionId string) ([]string, error) { k := []byte(auctionId + "_vp") var providers []string err := b.db.View(func(txn *badger.Txn) error { res, err := txn.Get(k) if err != nil { return err } err = res.Value(func(val []byte) error { dec := gob.NewDecoder(bytes.NewReader(val)) err := dec.Decode(&providers) return err }) return err }) if err != nil { if errors.Is(err, badger.ErrKeyNotFound) { return []string{}, nil } return nil, err } if providers == nil { return nil, errors.New("nil providers value") } return providers, nil } func (b *auctionDB) ClearVerifiedProviders(auctionId string) error { k := []byte(auctionId + "_vp") err := b.db.Update(func(txn *badger.Txn) error { if _, err := txn.Get(k); err != nil && errors.Is(err, badger.ErrKeyNotFound) { return err } return txn.Delete(k) }) return err }