package handler import ( "encoding/binary" "time" "ripple/bug" "ripple/commands" "ripple/config" "ripple/crypto" "ripple/helpers" "ripple/services" "ripple/state" "ripple/transport" "ripple/types" ) type AccountHandlers struct { st *state.State pm *services.PaymentManager accSender *transport.AccountTransport cptSender *transport.CounterpartTransport resilience *services.Resilience } func NewAccountHandlers( st *state.State, pm *services.PaymentManager, accSender *transport.AccountTransport, cptSender *transport.CounterpartTransport, resilience *services.Resilience, ) *AccountHandlers { return &AccountHandlers{ st: st, pm: pm, accSender: accSender, cptSender: cptSender, resilience: resilience, } } func (h *AccountHandlers) FindPath(id types.UserIdentifier, args []byte) { if len(args) < 62 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) amount := binary.BigEndian.Uint64(args[32:40]) inOrOutByte := args[40] penaltyRate := binary.BigEndian.Uint32(args[41:45]) fee := binary.BigEndian.Uint64(args[45:53]) hops := args[53] tax := binary.BigEndian.Uint64(args[54:62]) inOrOut := helpers.ByteToBool(inOrOutByte) if !inOrOut && h.pm.PreviewBandwidthIn(id) < amount+tax { return } pf, ok := h.st.Memory.GetPathfinding(paymentID) if !ok { if insufficientPenalty(amount, fee, penaltyRate) { return } if invalidTaxrate(tax, amount, h.st.Storage.User.TrustIndex) { return } if h.pm.AddPathfinding(paymentID, amount, inOrOut, penaltyRate, fee, hops, id, tax) { h.accSender.Send(id, helpers.BuildPathRecurse(paymentID, 0)) } return } if pf.Commit || pf.PathFound() || pf.Depth >= config.MaxDepth { return } if inOrOut != pf.InOrOut { if !inOrOut { pf.Incoming = id } else { pf.Outgoing = id pf.Hops = hops + 1 } h.st.Memory.Pathfinding[paymentID] = pf if pf.Counterpart.IsSet() { if pf.IsIncoming() { pf.SetCommit() h.st.Memory.Pathfinding[paymentID] = pf h.st.Memory.Commit[id][paymentID] = struct{}{} h.accSender.Send(id, helpers.BuildPreparePath(paymentID)) return } } h.accSender.Send(pf.Incoming, helpers.BuildPathFound(paymentID, pf.Hops)) return } if !inOrOut && pf.Incoming == id || inOrOut && pf.Outgoing == id { h.pm.ForwardFindPath(paymentID) } } func (h *AccountHandlers) PathRecurse(id types.UserIdentifier, args []byte) { if len(args) < 33 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) depth := args[32] pf, ok := h.st.Memory.GetPathfinding(paymentID) if !ok || pf.Commit || pf.PathFound() || depth < pf.Depth || depth >= config.MaxDepth { return } pf.Depth = depth + 1 h.st.Memory.Pathfinding[paymentID] = pf if pf.Counterpart.IsSet() { h.cptSender.Send(types.Instruction{Command: commands.COUNTERPART_FIND_PATH}) return } var prev types.UserIdentifier if pf.IsIncoming() { prev = pf.Incoming } else { prev = pf.Outgoing } h.accSender.Send(prev, helpers.BuildPathRecurse(paymentID, pf.Depth)) } func (h *AccountHandlers) PathFound(id types.UserIdentifier, args []byte) { if len(args) < 33 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) hops := args[32] pf, ok := h.st.Memory.GetPathfinding(paymentID) if !ok || pf.PathFound() || pf.Commit { return } if pf.Counterpart.IsSet() { if pf.IsOutgoing() { return } pf.Outgoing = id pf.Hops = hops+1 pf.SetCommit() h.st.Memory.Pathfinding[paymentID] = pf h.st.Memory.Commit[id][paymentID] = struct{}{} h.accSender.Send(id, helpers.BuildPreparePath(paymentID)) return } if pf.Incoming.IsEmpty() { return } pf.Outgoing = id pf.Hops = hops+1 h.st.Memory.Pathfinding[paymentID] = pf h.accSender.Send(pf.Incoming, helpers.BuildPathFound(paymentID, pf.Hops)) } func (h *AccountHandlers) PreparePath(id types.UserIdentifier, args []byte) { if len(args) < 32 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) pf, ok := h.st.Memory.GetPathfinding(paymentID) if !ok || pf.Commit || h.pm.PreviewBandwidthIn(id) < pf.Amount+pf.FeeIn()+pf.Tax { return } pf.Incoming = id pf.SetCommit() h.st.Memory.Pathfinding[paymentID] = pf h.st.Memory.Commit[id][paymentID] = struct{}{} if pf.Counterpart.IsSet() { if pf.IsIncoming() { return } h.cptSender.Send(types.Instruction{Command: commands.COUNTERPART_COMMIT_PAYMENT}) return } if pf.Outgoing.IsEmpty() { return } h.st.Memory.Commit[pf.Outgoing][paymentID] = struct{}{} h.accSender.Send(pf.Outgoing, helpers.BuildPreparePath(paymentID)) } func (h *AccountHandlers) CommitPayment(id types.UserIdentifier, args []byte) { if len(args) < 32 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) pf, ok := h.st.Memory.GetPathfinding(paymentID) if !ok || !pf.Commit || id != pf.Incoming { return } if h.st.Storage.GetBandwidthIn(id) < int64(pf.Amount+pf.FeeIn()) { panic(bug.BugStateViolated) } h.st.Storage.MustSetPending(id, paymentID) payment := state.Payment{ Amount: pf.Amount, Incoming: id, PenaltyRate: pf.PenaltyRate, FeeIn: pf.FeeIn(), CreatedAt: time.Now().Unix(), Tax: pf.Tax, } if pf.Counterpart.IsSet() { payment.Counterpart = pf.Counterpart payment.Preimage = h.st.Memory.Preimage h.st.Storage.Payments[paymentID] = payment h.st.MustSave() h.st.Memory.RemovePathfinding(paymentID) h.cptSender.Send(types.Instruction{Command: commands.COUNTERPART_SEAL_PAYMENT}) return } if pf.Outgoing.IsEmpty() { panic(bug.BugStateViolated) } h.st.Storage.MustSetPending(pf.Outgoing, paymentID) h.st.Memory.RemovePathfinding(paymentID) payment.Outgoing = pf.Outgoing payment.FeeOut = pf.FeeOut() h.st.Storage.Payments[paymentID] = payment h.st.MustSave() h.accSender.Send(pf.Outgoing, helpers.BuildCommitPayment(paymentID)) } func (h *AccountHandlers) SealPayment(id types.UserIdentifier, args []byte) { if len(args) < 32 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) payment, ok := h.st.Storage.Payments[paymentID] if !ok || !payment.Committing() || payment.Incoming != id { return } penaltyTicker := payment.PenaltyTicker() payment.CommitPenalty = penaltyTicker if penaltyTicker >= payment.Amount { payment.Cleanup() h.st.Storage.Payments[paymentID] = payment h.st.MustSave() h.accSender.Send(payment.Incoming, helpers.BuildCleanupPayment(paymentID)) if payment.Outgoing.IsSet() { payment.FinalizeOut = payment.Amount+payment.FeeOut+payment.Tax h.st.Storage.Payments[paymentID] = payment h.st.MustSave() h.accSender.Send(payment.Outgoing, helpers.BuildCleanupPayment(paymentID)) } return } payment.Seal() h.st.Storage.Payments[paymentID] = payment h.st.MustSave() if payment.Counterpart.IsSet() { payment.Finalize() h.st.Storage.Payments[paymentID] = payment h.st.MustSave() h.accSender.Send(payment.Incoming, helpers.BuildFinalizePayment(paymentID, payment.Preimage)) return } h.accSender.Send(payment.Outgoing, helpers.BuildSealPayment(paymentID)) } func (h *AccountHandlers) FinalizePayment(id types.UserIdentifier, args []byte) { if len(args) < 64 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) var preimage [32]byte copy(preimage[:], args[32:64]) if crypto.Sha256(preimage[:]) != paymentID { return } payment, ok := h.st.Storage.Payments[paymentID] if !ok || payment.Finalizing() || payment.Cleaning() { h.accSender.Send(id, helpers.BuildAckPreimage(paymentID)) return } if !payment.Sealing() || payment.Outgoing != id { return } payment.FinalizeOut = helpers.FinalizeOut(payment) payment.Finalize() payment.Preimage = preimage h.st.Storage.Payments[paymentID] = payment h.st.MustSave() h.accSender.Send(id, helpers.BuildSyncPayment(paymentID, payment.FinalizeOut)) if payment.Counterpart.IsSet() { h.accSender.Send(id, helpers.BuildAckPreimage(paymentID)) return } h.accSender.Send(id, helpers.BuildAckPreimage(paymentID)) h.accSender.Send(payment.Incoming, helpers.BuildFinalizePayment(paymentID, payment.Preimage)) } func (h *AccountHandlers) CancelPayment(id types.UserIdentifier, args []byte) { if len(args) < 64 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) var preimage [32]byte copy(preimage[:], args[32:64]) if crypto.Sha256(preimage[:]) != paymentID { return } payment, ok := h.st.Storage.Payments[paymentID] if !ok || payment.Cancelling() || payment.Cleaning() { h.accSender.Send(id, helpers.BuildAckPreimage(paymentID)) return } if !payment.Committing() || payment.Incoming != id { return } payment.Preimage = preimage payment.Cancel() if payment.Outgoing.IsSet() { payment.FinalizeOut = helpers.CancelOut(payment) } h.st.Storage.Payments[paymentID] = payment h.st.MustSave() if payment.Outgoing.IsSet() { h.accSender.Send(payment.Outgoing, helpers.BuildCancelPayment(paymentID, payment.Preimage)) } h.accSender.Send(payment.Incoming, helpers.BuildAckPreimage(paymentID)) } func (h *AccountHandlers) CleanupPayment(id types.UserIdentifier, args []byte) { if len(args) < 32 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) payment, ok := h.st.Storage.Payments[paymentID] if !ok || payment.Syncing() { return } if payment.PenaltyTicker() < payment.Amount { return } if payment.Committing() { payment.CommitPenalty = payment.Amount } payment.Cleanup() if payment.Outgoing.IsSet() { payment.FinalizeOut = helpers.CleanupOut(payment) h.st.Storage.Payments[paymentID] = payment h.st.MustSave() if payment.Outgoing != id { h.accSender.Send(payment.Outgoing, helpers.BuildCleanupPayment(paymentID)) } } h.st.Storage.Payments[paymentID] = payment h.st.MustSave() if payment.Incoming.IsSet() && payment.Incoming != id { h.accSender.Send(payment.Incoming, helpers.BuildCleanupPayment(paymentID)) } } func (h *AccountHandlers) AckPreimage(id types.UserIdentifier, args []byte) { if len(args) < 32 { return } var paymentID [32]byte copy(paymentID[:], args) payment, ok := h.st.Storage.Payments[paymentID] if !ok || payment.AckPreimage || !(payment.Finalizing() && id == payment.Incoming || payment.Cancelling() && id == payment.Outgoing) { return } payment.AckPreimage = true if payment.SynchronizedIn() { payment.Incoming = types.UserIdentifier{} } if payment.SynchronizedOut() { payment.Outgoing = types.UserIdentifier{} } h.st.Storage.Payments[paymentID] = payment if payment.Incoming.IsEmpty() && payment.Outgoing.IsEmpty() { delete(h.st.Storage.Payments, paymentID) } h.st.MustSave() } func (h *AccountHandlers) SyncPayment(id types.UserIdentifier, args []byte) { if len(args) < 40 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) amount := binary.BigEndian.Uint64(args[32:40]) payment, ok := h.st.Storage.Payments[paymentID] if !ok { h.accSender.Send(id, helpers.BuildAckSync(paymentID)) return } if !payment.Syncing() || (payment.Incoming.IsSet() && payment.Incoming != id) || amount > payment.Amount+payment.FeeIn+payment.Tax { return } if payment.Incoming == id && !payment.Synced { h.st.MustFinalizeIncoming(payment.Incoming, paymentID, amount, payment.GetTaxRate()) payment.Synced = true if payment.SynchronizedIn() { payment.Incoming = types.UserIdentifier{} } h.st.Storage.Payments[paymentID] = payment if payment.Finalizing() && payment.Counterpart.IsSet() { h.st.Storage.AddReceipt(state.Receipt{ Identifier: paymentID, Counterpart: payment.Counterpart, Amount: int64(payment.Amount), Timestamp: time.Now().Unix(), }) } h.st.MustSave() h.resilience.Redistribute(payment.Incoming, calculateTax(payment), payment.GetTaxRate()) } if payment.Incoming.IsEmpty() && payment.Outgoing.IsEmpty() { delete(h.st.Storage.Payments, paymentID) h.st.MustSave() } h.accSender.Send(id, helpers.BuildAckSync(paymentID)) } func (h *AccountHandlers) AckSync(id types.UserIdentifier, args []byte) { if len(args) < 32 { return } var paymentID [32]byte copy(paymentID[:], args) payment, ok := h.st.Storage.Payments[paymentID] if !ok || !payment.Syncing() || payment.AckSync || payment.Outgoing != id { return } payment.AckSync = true h.st.Storage.MustFinalizeOutgoing(payment, paymentID) if payment.Finalizing() && payment.Counterpart.IsSet() { h.st.Storage.AddReceipt(state.Receipt{ Identifier: paymentID, Counterpart: payment.Counterpart, Amount: -int64(payment.Amount), Timestamp: time.Now().Unix(), }) } if payment.SynchronizedOut() { payment.Outgoing = types.UserIdentifier{} } h.st.Storage.Payments[paymentID] = payment if payment.Incoming.IsEmpty() && payment.Outgoing.IsEmpty() { delete(h.st.Storage.Payments, paymentID) } h.st.MustSave() } func (h *AccountHandlers) VerifyCommit(id types.UserIdentifier, args []byte) { if len(args) < 32 { return } var paymentID [32]byte copy(paymentID[:], args) payment, ok := h.st.Storage.Payments[paymentID] if !ok { h.accSender.Send(id, helpers.BuildAckCommit(paymentID, 1)) return } if payment.Incoming.IsSet() && payment.Incoming != id { return } h.accSender.Send(id, helpers.BuildAckCommit(paymentID, 0)) } func (h *AccountHandlers) AckCommit(id types.UserIdentifier, args []byte) { if len(args) < 33 { return } var paymentID [32]byte copy(paymentID[:], args[:32]) status := args[32] payment, ok := h.st.Storage.Payments[paymentID] if !ok || payment.CommitOut || id != payment.Outgoing { return } if status == 0 { payment.CommitOut = true h.st.Storage.Payments[paymentID] = payment } else { payment.Outgoing = types.UserIdentifier{} h.st.Storage.RemovePending(id, paymentID) h.st.Storage.Payments[paymentID] = payment if payment.Incoming.IsEmpty() { delete(h.st.Storage.Payments, paymentID) } } h.st.MustSave() } func (h *AccountHandlers) SwarmRedistribution(id types.UserIdentifier, args []byte) { if len(args) < 16 { return } amount := binary.BigEndian.Uint64(args[0:8]) counter := binary.BigEndian.Uint64(args[8:16]) account := h.st.Storage.MustGetAccount(id) if counter > account.SyncIn { if account.Balance+int64(amount) > 0 { return } account.Balance += int64(amount) account.SyncIn = counter h.st.Storage.Accounts[id] = account h.st.MustSave() h.resilience.Redistribute(id, amount, account.Width) } h.accSender.Send(id, helpers.BuildRedistributionAck(counter)) } func (h *AccountHandlers) RedistributionAck(id types.UserIdentifier, args []byte) { if len(args) < 8 { return } counter := binary.BigEndian.Uint64(args[0:8]) account := h.st.Storage.MustGetAccount(id) if account.TaxSyncing > 0 && counter == account.SyncOut { if account.Balance-int64(account.TaxSyncing) < 0 { panic(bug.BugStateViolated) } account.Balance -= int64(account.TaxSyncing) account.TaxSyncing = 0 h.st.Storage.Accounts[id] = account h.st.MustSave() } }