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 }