decred.org/dcrdex@v1.0.5/tatanka/db/reputation.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package db 5 6 import ( 7 "encoding" 8 "encoding/binary" 9 "fmt" 10 "time" 11 12 "decred.org/dcrdex/dex" 13 "decred.org/dcrdex/tatanka/tanka" 14 ) 15 16 // Reputation entries with key encoded as 17 // peer_id | 6_bytes_millisecond_timestamp | 32_byte_penalty_id 18 // Value is empty if it's a success. For penalties, the value is 19 // 1_byte_score_decrement | encoded_penalty_proof. 20 21 func (d *DB) Reputation(peerID tanka.PeerID) (rep *tanka.Reputation, err error) { 22 rep = &tanka.Reputation{ 23 Points: make([]int8, 0, tanka.MaxReputationEntries), 24 } 25 return rep, d.reputationDB.ForEach(func(k, v []byte) error { 26 if len(v) == 0 { 27 // Success 28 rep.Score++ 29 rep.Points = append(rep.Points, 1) 30 return nil 31 } 32 rep.Score -= int16(v[0]) 33 rep.Points = append(rep.Points, -int8(v[0])) 34 return nil 35 }, WithPrefix(peerID[:]), WithReverse(), WithMaxEntries(tanka.MaxReputationEntries, true)) 36 } 37 38 func (d *DB) RegisterSuccess(peerID tanka.PeerID, stamp time.Time, refID [32]byte) error { 39 const keyLength = tanka.PeerIDLength + 6 + 32 40 k := make([]byte, keyLength) 41 copy(k[:tanka.PeerIDLength], peerID[:]) 42 stampB := make([]byte, 8) 43 binary.BigEndian.PutUint64(stampB, uint64(stamp.UnixMilli())) 44 copy(k[tanka.PeerIDLength:tanka.PeerIDLength+6], stampB[2:]) 45 copy(k[tanka.PeerIDLength+6:], refID[:]) 46 47 if err := d.reputationDB.Store(k, dex.Bytes{}); err != nil { 48 return fmt.Errorf("error storing success for user %q: %v", peerID, err) 49 } 50 51 // TODO: Periodically run a nanny function to clear extra entries. Otherwise 52 // entries are only deleted during the score scan. So theoretically, someone 53 // could fill the db with a billion successes. Same in RegisterPenalty. 54 55 return nil 56 } 57 58 func (d *DB) RegisterPenalty(peerID tanka.PeerID, stamp time.Time, penaltyID [32]byte, scoreDecrement uint8, penaltyInfo encoding.BinaryMarshaler) error { 59 const keyLength = tanka.PeerIDLength + 6 + 32 60 k := make([]byte, keyLength) 61 copy(k[:tanka.PeerIDLength], peerID[:]) 62 stampB := make([]byte, 8) 63 binary.BigEndian.PutUint64(stampB, uint64(stamp.UnixMilli())) 64 copy(k[tanka.PeerIDLength:tanka.PeerIDLength+6], stampB[2:]) 65 copy(k[tanka.PeerIDLength+6:], penaltyID[:]) 66 67 b, err := penaltyInfo.MarshalBinary() 68 if err != nil { 69 return fmt.Errorf("error marshaling penalty info: %w", err) 70 } 71 v := make(dex.Bytes, 1+len(b)) 72 v[0] = scoreDecrement 73 copy(v[1:], b) 74 if err := d.reputationDB.Store(k, v); err != nil { 75 return fmt.Errorf("error storing penalty: %w", err) 76 } 77 return nil 78 }