Skip to content
This repository has been archived by the owner on Sep 12, 2019. It is now read-only.

Send account_merge operations to callbacks.receive #91

Merged
merged 1 commit into from
Jan 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/github.com/stellar/gateway/horizon/effect_response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package horizon

// EffectsPageResponse contains page of effects returned by Horizon
type EffectsPageResponse struct {
Embedded struct {
Records []EffectResponse
} `json:"_embedded"`
}

// EffectResponse contains effect data returned by Horizon
type EffectResponse struct {
Type string `json:"type"`
Amount string `json:"amount"`
}
29 changes: 29 additions & 0 deletions src/github.com/stellar/gateway/horizon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"strings"
"time"

"github.com/stellar/go/support/errors"
"github.com/stellar/go/xdr"
)

Expand All @@ -23,6 +24,7 @@ type PaymentHandler func(PaymentResponse) error
type HorizonInterface interface {
LoadAccount(accountID string) (response AccountResponse, err error)
LoadMemo(p *PaymentResponse) (err error)
LoadAccountMergeAmount(p *PaymentResponse) error
LoadOperation(operationID string) (response PaymentResponse, err error)
StreamPayments(accountID string, cursor *string, onPaymentHandler PaymentHandler) (err error)
SubmitTransaction(txeBase64 string) (response SubmitTransactionResponse, err error)
Expand Down Expand Up @@ -125,6 +127,33 @@ func (h *Horizon) LoadMemo(p *PaymentResponse) (err error) {
return json.NewDecoder(res.Body).Decode(&p.Memo)
}

// LoadAccountMergeAmount loads `account_merge` operation amount from it's effects
func (h *Horizon) LoadAccountMergeAmount(p *PaymentResponse) error {
if p.Type != "account_merge" {
return errors.New("Not `account_merge` operation")
}

res, err := http.Get(p.Links.Effects.Href)
if err != nil {
return errors.Wrap(err, "Error getting effects for operation")
}
defer res.Body.Close()
var page EffectsPageResponse
err = json.NewDecoder(res.Body).Decode(&page)
if err != nil {
return errors.Wrap(err, "Error decoding effects page")
}

for _, effect := range page.Embedded.Records {
if effect.Type == "account_credited" {
p.Amount = effect.Amount
return nil
}
}

return errors.New("Could not find `account_credited` effect in `account_merge` operation effects")
}

// StreamPayments streams incoming payments
func (h *Horizon) StreamPayments(accountID string, cursor *string, onPaymentHandler PaymentHandler) (err error) {
url := h.ServerURL + "/accounts/" + accountID + "/payments"
Expand Down
7 changes: 7 additions & 0 deletions src/github.com/stellar/gateway/horizon/payment_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ type PaymentResponse struct {
Transaction struct {
Href string `json:"href"`
} `json:"transaction"`
Effects struct {
Href string `json:"href"`
} `json:"effects"`
} `json:"_links"`

// payment/path_payment fields
Expand All @@ -20,6 +23,10 @@ type PaymentResponse struct {
AssetIssuer string `json:"asset_issuer"`
Amount string `json:"amount"`

// account_merge
Account string `json:"account"`
Into string `json:"into"`

// transaction fields
Memo struct {
Type string `json:"memo_type"`
Expand Down
19 changes: 17 additions & 2 deletions src/github.com/stellar/gateway/listener/payment_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,15 @@ func (pl *PaymentListener) onPayment(payment horizon.PaymentResponse) (err error
// shouldProcessPayment returns false and text status if payment should not be processed
// (ex. asset is different than allowed assets).
func (pl *PaymentListener) shouldProcessPayment(payment horizon.PaymentResponse) (bool, string) {
if payment.Type != "payment" && payment.Type != "path_payment" {
if payment.Type != "payment" && payment.Type != "path_payment" && payment.Type != "account_merge" {
return false, "Not a payment operation"
}

if payment.To != pl.config.Accounts.ReceivingAccountID {
if payment.Type == "account_merge" {
payment.AssetType = "native"
}

if payment.To != pl.config.Accounts.ReceivingAccountID && payment.Into != pl.config.Accounts.ReceivingAccountID {
return false, "Operation sent not received"
}

Expand All @@ -229,6 +233,17 @@ func (pl *PaymentListener) shouldProcessPayment(payment horizon.PaymentResponse)
}

func (pl *PaymentListener) process(payment horizon.PaymentResponse) error {
if payment.Type == "account_merge" {
payment.AssetType = "native"
payment.From = payment.Account
payment.To = payment.Into

err := pl.horizon.LoadAccountMergeAmount(&payment)
if err != nil {
return errors.Wrap(err, "Unable to load account_merge amount")
}
}

err := pl.horizon.LoadMemo(&payment)
if err != nil {
return errors.Wrap(err, "Unable to load transaction memo")
Expand Down
54 changes: 54 additions & 0 deletions src/github.com/stellar/gateway/listener/payment_listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,60 @@ func TestPaymentListener(t *testing.T) {
})
})

Convey("When receive callback returns success (account_merge)", func() {
operation.Type = "account_merge"
operation.Account = "GBL27BKG2JSDU6KQ5YJKCDWTVIU24VTG4PLB63SF4K2DBZS5XZMWRPVU"
operation.Into = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
operation.Amount = "100"
operation.Memo.Type = "text"
operation.Memo.Value = "testing"

// Updated in the listener
operation.From = "GBL27BKG2JSDU6KQ5YJKCDWTVIU24VTG4PLB63SF4K2DBZS5XZMWRPVU"
operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
operation.AssetType = "native"

config.Assets[1].Code = "XLM"
config.Assets[1].Issuer = ""

mockRepository.On("GetReceivedPaymentByOperationID", int64(1)).Return(nil, nil).Once()
mockHorizon.On("LoadAccountMergeAmount", &operation).Return(nil).Once()
mockHorizon.On("LoadMemo", &operation).Return(nil).Once()

mockEntityManager.On("Persist", mock.AnythingOfType("*entities.ReceivedPayment")).
Run(ensurePaymentStatus(t, operation, "Processing...")).Return(nil).Once()

mockEntityManager.On("Persist", mock.AnythingOfType("*entities.ReceivedPayment")).
Run(ensurePaymentStatus(t, operation, "Success")).Return(nil).Once()

mockHTTPClient.On(
"Do",
mock.MatchedBy(func(req *http.Request) bool {
return req.URL.String() == "http://receive_callback"
}),
).Return(
net.BuildHTTPResponse(200, "ok"),
nil,
).Run(func(args mock.Arguments) {
req := args.Get(0).(*http.Request)

assert.Equal(t, operation.Account, req.PostFormValue("from"))
assert.Equal(t, operation.Amount, req.PostFormValue("amount"))
assert.Equal(t, operation.AssetCode, req.PostFormValue("asset_code"))
assert.Equal(t, operation.AssetIssuer, req.PostFormValue("asset_issuer"))
assert.Equal(t, operation.Memo.Type, req.PostFormValue("memo_type"))
assert.Equal(t, operation.Memo.Value, req.PostFormValue("memo"))
}).Once()

Convey("it should save the status", func() {
err := paymentListener.onPayment(operation)
assert.Nil(t, err)
mockHorizon.AssertExpectations(t)
mockEntityManager.AssertExpectations(t)
mockRepository.AssertExpectations(t)
})
})

Convey("When receive callback returns success (no memo)", func() {
operation.Type = "payment"
operation.To = "GATKP6ZQM5CSLECPMTAC5226PE367QALCPM6AFHTSULPPZMT62OOPMQB"
Expand Down
6 changes: 6 additions & 0 deletions src/github.com/stellar/gateway/mocks/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ func (m *MockHorizon) LoadMemo(p *horizon.PaymentResponse) (err error) {
return a.Error(0)
}

// LoadAccountMergeAmount is a mocking a method
func (m *MockHorizon) LoadAccountMergeAmount(p *horizon.PaymentResponse) (err error) {
a := m.Called(p)
return a.Error(0)
}

// StreamPayments is a mocking a method
func (m *MockHorizon) StreamPayments(accountID string, cursor *string, onPaymentHandler horizon.PaymentHandler) (err error) {
a := m.Called(accountID, cursor, onPaymentHandler)
Expand Down