github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/hermes_promise_storage.go (about)

     1  /*
     2   * Copyright (C) 2019 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package pingpong
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"sync"
    25  
    26  	"github.com/asdine/storm/v3/codec/json"
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/mysteriumnetwork/node/core/storage/boltdb"
    29  	"github.com/mysteriumnetwork/node/identity"
    30  	"github.com/mysteriumnetwork/payments/crypto"
    31  	"go.etcd.io/bbolt"
    32  )
    33  
    34  const hermesPromiseBucketName = "hermes_promises"
    35  
    36  // ErrAttemptToOverwrite occurs when a promise with lower value is attempted to be overwritten on top of an existing promise.
    37  var ErrAttemptToOverwrite = errors.New("attempted to overwrite a promise with and equal or lower value")
    38  
    39  // HermesPromiseStorage allows for storing of hermes promises.
    40  type HermesPromiseStorage struct {
    41  	lock sync.Mutex
    42  	bolt *boltdb.Bolt
    43  }
    44  
    45  // NewHermesPromiseStorage returns a new instance of the hermes promise storage.
    46  func NewHermesPromiseStorage(bolt *boltdb.Bolt) *HermesPromiseStorage {
    47  	return &HermesPromiseStorage{
    48  		bolt: bolt,
    49  	}
    50  }
    51  
    52  // HermesPromise represents a promise we store from the hermes
    53  type HermesPromise struct {
    54  	ChannelID   string
    55  	Identity    identity.Identity
    56  	HermesID    common.Address
    57  	Promise     crypto.Promise
    58  	R           string
    59  	Revealed    bool
    60  	AgreementID *big.Int
    61  }
    62  
    63  // Store stores the given promise.
    64  func (aps *HermesPromiseStorage) Store(promise HermesPromise) error {
    65  	aps.lock.Lock()
    66  	defer aps.lock.Unlock()
    67  
    68  	previousPromise, err := aps.get(promise.Promise.ChainID, promise.ChannelID)
    69  	if err != nil && !errors.Is(err, ErrNotFound) {
    70  		return err
    71  	}
    72  
    73  	if promise.Promise.Amount == nil {
    74  		promise.Promise.Amount = big.NewInt(0)
    75  	}
    76  
    77  	if !aps.shouldOverride(previousPromise, promise) {
    78  		return ErrAttemptToOverwrite
    79  	}
    80  
    81  	if err := aps.bolt.SetValue(aps.getBucketName(promise.Promise.ChainID), promise.ChannelID, promise); err != nil {
    82  		return fmt.Errorf("could not store hermes promise: %w", err)
    83  	}
    84  	return nil
    85  }
    86  
    87  func (aps *HermesPromiseStorage) shouldOverride(old, new HermesPromise) bool {
    88  	if old.Promise.Amount == nil {
    89  		return true
    90  	}
    91  
    92  	if old.Promise.Amount.Cmp(new.Promise.Amount) > 0 {
    93  		return false
    94  	}
    95  
    96  	return true
    97  }
    98  
    99  // Delete deletes the given hermes promise.
   100  func (aps *HermesPromiseStorage) Delete(promise HermesPromise) error {
   101  	return aps.bolt.DeleteKey(aps.getBucketName(promise.Promise.ChainID), promise.ChannelID)
   102  }
   103  
   104  func (aps *HermesPromiseStorage) get(chainID int64, channelID string) (HermesPromise, error) {
   105  	result := &HermesPromise{}
   106  	err := aps.bolt.GetValue(aps.getBucketName(chainID), channelID, result)
   107  	if err != nil {
   108  		if err.Error() == errBoltNotFound {
   109  			err = ErrNotFound
   110  		} else {
   111  			err = fmt.Errorf("could not get hermes promise: %w", err)
   112  		}
   113  	}
   114  	return *result, err
   115  }
   116  
   117  // Get fetches the promise by channel ID identifier.
   118  func (aps *HermesPromiseStorage) Get(chainID int64, channelID string) (HermesPromise, error) {
   119  	aps.lock.Lock()
   120  	defer aps.lock.Unlock()
   121  	return aps.get(chainID, channelID)
   122  }
   123  
   124  // HermesPromiseFilter defines all flags for filtering in promises in storage.
   125  type HermesPromiseFilter struct {
   126  	Identity *identity.Identity
   127  	HermesID *common.Address
   128  	ChainID  int64
   129  }
   130  
   131  func (aps *HermesPromiseStorage) getBucketName(chainID int64) string {
   132  	return fmt.Sprintf("%v_%v", hermesPromiseBucketName, chainID)
   133  }
   134  
   135  // List fetches the promise for the given hermes.
   136  func (aps *HermesPromiseStorage) List(filter HermesPromiseFilter) ([]HermesPromise, error) {
   137  	aps.lock.Lock()
   138  	defer aps.lock.Unlock()
   139  
   140  	result := make([]HermesPromise, 0)
   141  	aps.bolt.RLock()
   142  	defer aps.bolt.RUnlock()
   143  	err := aps.bolt.DB().Bolt.View(func(tx *bbolt.Tx) error {
   144  		bucket := tx.Bucket([]byte(aps.getBucketName(filter.ChainID)))
   145  		if bucket == nil {
   146  			return nil
   147  		}
   148  
   149  		return bucket.ForEach(func(k, v []byte) error {
   150  			if string(k) == "__storm_metadata" {
   151  				return nil
   152  			}
   153  
   154  			var entry HermesPromise
   155  			if err := json.Codec.Unmarshal(v, &entry); err != nil {
   156  				return err
   157  			}
   158  
   159  			if filter.Identity != nil {
   160  				if *filter.Identity != entry.Identity {
   161  					return nil
   162  				}
   163  			}
   164  			if filter.HermesID != nil {
   165  				if *filter.HermesID != entry.HermesID {
   166  					return nil
   167  				}
   168  			}
   169  
   170  			result = append(result, entry)
   171  			return nil
   172  		})
   173  	})
   174  	if err != nil {
   175  		return nil, fmt.Errorf("could not list hermes promises: %w", err)
   176  	}
   177  
   178  	return result, nil
   179  }