package state import ( "time" "ripple/bug" "ripple/config" "ripple/errmsgs" "ripple/types" ) const ( Commit = 0 SealIn = 1 Sealed = 2 Cancel = 3 ) type User struct { SecretKey [32]byte TrustIndex float32 } type Payment struct { Amount uint64 Incoming types.UserIdentifier Outgoing types.UserIdentifier Counterpart types.UserIdentifier PenaltyRate uint32 CommitPenalty uint64 FeeIn uint64 FeeOut uint64 Tax uint64 Preimage [32]byte Status byte CreatedAt int64 } type Sync struct { TurnBit byte TurnCounter uint32 LastValidated types.Instruction } type Account struct { Creditline int64 Width float32 Pending map[[32]byte]struct{} TrustlineIn uint64 TrustlineOut uint64 Sync } type Receipt struct { Identifier [32]byte Counterpart types.UserIdentifier Amount int64 Timestamp int64 } type Storage struct { User User Accounts map[types.UserIdentifier]Account Payments map[[32]byte]Payment Preimage [32]byte Receipts [config.BufferSize]Receipt } func (s *Storage) MustGetAccount(id types.UserIdentifier) Account { acc, ok := s.Accounts[id] if !ok { panic(bug.BugStateViolated) } return acc } func (s *Storage) AccountExists(id types.UserIdentifier) bool { _, ok := s.Accounts[id] return ok } func (s *Storage) AddAccount(id types.UserIdentifier, turnBit byte) error { if s.AccountExists(id) { panic(bug.BugStateViolated) } if len(s.Accounts) >= config.BufferSize { return errmsgs.ErrBufferFull } s.Accounts[id] = Account{ Sync: Sync{ TurnBit: turnBit, }, Pending: make(map[[32]byte]struct{}), } return nil } func (s *Storage) RemoveAccount(id types.UserIdentifier) { if !s.AccountExists(id) { panic(bug.BugStateViolated) } delete(s.Accounts, id) } func (s *Storage) MustGetPayment(paymentID [32]byte) Payment { payment, ok := s.Payments[paymentID] if !ok { panic(bug.BugStateViolated) } return payment } func (s *Storage) GetBandwidthOut(id types.UserIdentifier) int64 { acc := s.MustGetAccount(id) bandwidthOut := int64(acc.TrustlineIn) + acc.Creditline for paymentID := range acc.Pending { payment := s.MustGetPayment(paymentID) if payment.Outgoing == id { bandwidthOut -= int64(payment.Amount+payment.FeeOut+payment.Tax) } } return bandwidthOut } func (s *Storage) GetBandwidthIn(id types.UserIdentifier) int64 { acc := s.MustGetAccount(id) bandwidthIn := int64(acc.TrustlineOut) - acc.Creditline for paymentID := range acc.Pending { payment := s.MustGetPayment(paymentID) if payment.Incoming == id { bandwidthIn -= int64(payment.Amount+payment.FeeIn+payment.Tax) } } return bandwidthIn } func (s *Storage) AddReceipt(r Receipt) { copy(s.Receipts[1:], s.Receipts[:]) s.Receipts[0] = r } func (s *Storage) MustRemovePending(id types.UserIdentifier, paymentID [32]byte) { acc := s.MustGetAccount(id) delete(acc.Pending, paymentID) s.Accounts[id] = acc } func (s *Storage) MustFinalizeIncoming(userID types.UserIdentifier, paymentID [32]byte, amount uint64, taxrate float32) bool { account := s.MustGetAccount(userID) if _, ok := account.Pending[paymentID]; !ok { panic(bug.BugStateViolated) } negToPos := account.IncreaseCreditline(amount, taxrate) delete(account.Pending, paymentID) s.Accounts[userID] = account return negToPos } func (s *Storage) MustFinalizeOutgoing(userID types.UserIdentifier, paymentID [32]byte, amount uint64, taxrate float32) { account := s.MustGetAccount(userID) if _, ok := account.Pending[paymentID]; !ok { panic(bug.BugStateViolated) } account.DecreaseCreditline(amount, taxrate) delete(account.Pending, paymentID) s.Accounts[userID] = account } func (p *Payment) Committed() bool { return p.Status == Commit } func (p *Payment) Cancelled() bool { return p.Status == Cancel } func (p *Payment) SealIn() bool { return p.Status == SealIn } func (p *Payment) Sealed() bool { return p.Status == Sealed } func (p *Payment) PenaltyTicker() uint64 { now := time.Now().Unix() if now > p.CreatedAt && p.PenaltyRate > 0 { penalty := uint64((now - p.CreatedAt)/int64(p.PenaltyRate)) if penalty > p.Amount { return p.Amount } return penalty } return 0 } func (p *Payment) CalculateFeeIn(penalty uint64) uint64 { var fee uint64 if p.Amount > 0 { fee = p.FeeIn * penalty / p.Amount if fee > p.FeeIn { fee = p.FeeIn } } return fee } func (p *Payment) CalculateFeeOut(penalty uint64) uint64 { var fee uint64 if p.Amount > 0 { fee = p.FeeOut * penalty / p.Amount if fee > p.FeeOut { fee = p.FeeOut } } return fee } func (p *Payment) GetTaxRate(amount uint64) float32 { if p.Tax == 0 { return 0 } return float32(p.Tax)/float32(amount+p.Tax) } func (a *Account) IncreaseCreditline(amount uint64, taxrate float32) (negToPos bool) { newCreditLine := a.Creditline + int64(amount) if newCreditLine > 0 { var weight float32 if a.Creditline > 0 { weight = float32(a.Creditline) / float32(newCreditLine) a.Width = weight*a.Width + (1 - weight)*taxrate } else { a.Width = taxrate negToPos = true } } a.Creditline = newCreditLine return } func (a *Account) DecreaseCreditline(amount uint64, taxrate float32) { newCreditLine := a.Creditline - int64(amount) if newCreditLine < 0 { var weight float32 if a.Creditline < 0 { weight = float32(-a.Creditline) / float32(-newCreditLine) a.Width = weight*a.Width + (1 - weight)*taxrate } else { a.Width = taxrate } } a.Creditline = newCreditLine }