package handler import ( "encoding/binary" "errors" "time" "ripple/bug" "ripple/commands" "ripple/config" "ripple/errmsgs" "ripple/lockstep" "ripple/state" "ripple/types" ) var errInvalidStateTransition = errors.New("invalid state transition") func SetTrustlineInternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 8 { return nil, errmsgs.ErrInvalidArgs } value := binary.BigEndian.Uint64(args[:8]) account := st.Storage.MustGetAccount(userID) account.TrustlineOut = value st.Storage.Accounts[userID] = account return nil, nil } func SetTrustlineExternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 8 { return nil, errmsgs.ErrInvalidArgs } value := binary.BigEndian.Uint64(args[:8]) account := st.Storage.MustGetAccount(userID) account.TrustlineIn = value st.Storage.Accounts[userID] = account return nil, nil } func CommitPaymentInternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 40 { return nil, errmsgs.ErrInvalidArgs } var paymentID [32]byte copy(paymentID[:], args[:32]) amount := binary.BigEndian.Uint64(args[32:40]) if st.Storage.GetBandwidthOut(userID) < int64(amount) { return nil, errInvalidStateTransition } { account := st.Storage.MustGetAccount(userID) account.Pending[paymentID] = struct{}{} st.Storage.Accounts[userID] = account } payment, ok := st.Storage.Payments[paymentID] if !ok { pf, ok := st.Memory.GetPathfinding(paymentID) if !ok { st.Storage.Payments[paymentID] = state.Payment{ Amount: amount, Outgoing: userID, Cancel: true, } return func(i *lockstep.Interlockstep) { i.Lockstep.Enqueue(userID, types.Instruction{ Command: commands.LOCKSTEP_CANCEL_PAYMENT, Arguments: paymentID[:], }) }, nil } delete(st.Memory.Pathfinding, paymentID) st.Storage.Payments[paymentID] = state.Payment{ Amount: amount, Outgoing: userID, Counterpart: pf.Counterpart, Timeout: time.Now().Add(config.Timeout).Unix(), } return nil, nil } payment.Outgoing = userID st.Storage.Payments[paymentID] = payment return nil, nil } func CommitPaymentExternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 40 { return nil, errmsgs.ErrInvalidArgs } var paymentID [32]byte copy(paymentID[:], args[:32]) amount := binary.BigEndian.Uint64(args[32:40]) if st.Storage.GetBandwidthIn(userID) < int64(amount) { return nil, errInvalidStateTransition } { account := st.Storage.MustGetAccount(userID) account.Pending[paymentID] = struct{}{} st.Storage.Accounts[userID] = account } pf, ok := st.Memory.GetPathfinding(paymentID) if !ok { st.Storage.Payments[paymentID] = state.Payment{ Amount: amount, Incoming: userID, Cancel: true, } return func(i *lockstep.Interlockstep) { i.Lockstep.Enqueue(userID, types.Instruction{ Command: commands.LOCKSTEP_CANCEL_PAYMENT, Arguments: paymentID[:], }) }, nil } delete(st.Memory.Pathfinding, paymentID) if pf.Counterpart != (types.UserIdentifier{}) { st.Storage.Payments[paymentID] = state.Payment{ Amount: amount, Incoming: userID, Counterpart: pf.Counterpart, } return func(i *lockstep.Interlockstep) { i.CptSender.Send(types.Instruction{Command: commands.COUNTERPART_FINALIZE_PAYMENT}) }, nil } st.Storage.Payments[paymentID] = state.Payment{ Amount: amount, Incoming: userID, Outgoing: pf.Outgoing, } return func(i *lockstep.Interlockstep) { i.Lockstep.Enqueue(pf.Outgoing, types.Instruction{ Command: commands.LOCKSTEP_COMMIT_PAYMENT, Arguments: args, }) }, nil } func FinalizePaymentInternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 32 { return nil, errmsgs.ErrInvalidArgs } var paymentID [32]byte copy(paymentID[:], args[:32]) payment, ok := st.Storage.Payments[paymentID] if !ok { panic(bug.BugStateViolated) } if payment.Cancel || payment.Outgoing != userID { return nil, errInvalidStateTransition } account := st.Storage.MustGetAccount(userID) if _, ok := account.Pending[paymentID]; !ok { panic(bug.BugStateViolated) } account.Creditline -= int64(payment.Amount) delete(account.Pending, paymentID) st.Storage.Accounts[userID] = account if payment.Counterpart != (types.UserIdentifier{}) { st.Storage.AddReceipt(state.Receipt{ Identifier: paymentID, Counterpart: payment.Counterpart, Amount: -int64(payment.Amount), Timestamp: time.Now().Unix(), }) } delete(st.Storage.Payments, paymentID) return nil, nil } func FinalizePaymentExternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 32 { return nil, errmsgs.ErrInvalidArgs } var paymentID [32]byte copy(paymentID[:], args[:32]) payment, ok := st.Storage.Payments[paymentID] if !ok || payment.Cancel || payment.Incoming != userID { return nil, errInvalidStateTransition } account := st.Storage.MustGetAccount(userID) if _, ok := account.Pending[paymentID]; !ok { panic(bug.BugStateViolated) } account.Creditline += int64(payment.Amount) delete(account.Pending, paymentID) st.Storage.Accounts[userID] = account if payment.Counterpart != (types.UserIdentifier{}) { st.Storage.AddReceipt(state.Receipt{ Identifier: paymentID, Counterpart: payment.Counterpart, Amount: int64(payment.Amount), Timestamp: time.Now().Unix(), }) delete(st.Storage.Payments, paymentID) return nil, nil } payment.Incoming = types.UserIdentifier{} st.Storage.Payments[paymentID] = payment return func(i *lockstep.Interlockstep) { i.Lockstep.Enqueue(payment.Outgoing, types.Instruction{ Command: commands.LOCKSTEP_FINALIZE_PAYMENT, Arguments: paymentID[:], }) }, nil } func CancelPaymentInternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 32 { return nil, errmsgs.ErrInvalidArgs } var paymentID [32]byte copy(paymentID[:], args[:32]) payment, ok := st.Storage.Payments[paymentID] if !ok || !payment.Cancel { panic(bug.BugStateViolated) } if payment.Incoming != userID && payment.Outgoing != userID { return nil, errInvalidStateTransition } account := st.Storage.MustGetAccount(userID) if _, ok := account.Pending[paymentID]; !ok { panic(bug.BugStateViolated) } delete(account.Pending, paymentID) st.Storage.Accounts[userID] = account delete(st.Storage.Payments, paymentID) return nil, nil } func CancelPaymentExternal(st *state.State, userID types.UserIdentifier, args []byte) (lockstep.InterLockstepCb, error) { if len(args) < 32 { return nil, errmsgs.ErrInvalidArgs } var paymentID [32]byte copy(paymentID[:], args[:32]) payment, ok := st.Storage.Payments[paymentID] if !ok { return nil, errInvalidStateTransition } account := st.Storage.MustGetAccount(userID) if _, ok := account.Pending[paymentID]; !ok { panic(bug.BugStateViolated) } delete(account.Pending, paymentID) st.Storage.Accounts[userID] = account if payment.Incoming == userID { if payment.Outgoing != (types.UserIdentifier{}) { payment.Incoming = types.UserIdentifier{} } else { delete(st.Storage.Payments, paymentID) return nil, nil } } else if payment.Outgoing == userID { if payment.Incoming != (types.UserIdentifier{}) { payment.Outgoing = types.UserIdentifier{} } else { delete(st.Storage.Payments, paymentID) return nil, nil } } else { return nil, errInvalidStateTransition } payment.Cancel = true st.Storage.Payments[paymentID] = payment return func(i *lockstep.Interlockstep) { i.Lockstep.Enqueue(payment.Outgoing, types.Instruction{ Command: commands.LOCKSTEP_CANCEL_PAYMENT, Arguments: paymentID[:], }) }, nil }