before, err := createAccount.StateBefore(masterAccount.LedgerKey(), 0) Expect(err).ToNot(HaveOccurred()) Expect(before).ToNot(BeNil()) after, err := createAccount.StateAfter(masterAccount.LedgerKey(), 0) Expect(err).ToNot(HaveOccurred()) Expect(after).ToNot(BeNil()) Expect(before.Data.MustAccount().Balance).To(BeNumerically(">", after.Data.MustAccount().Balance)) }) }) Context("Trustlines", func() { var tlkey xdr.LedgerKey var line xdr.Asset BeforeEach(func() { line.SetCredit("USD", gatewayAccount) tlkey.SetTrustline(newAccount, line) }) It("properly returns the state of a trustlines that gets removed", func() { before, err := removeTrustline.StateBefore(tlkey, 0) Expect(err).ToNot(HaveOccurred()) Expect(before).ToNot(BeNil()) tl := before.Data.MustTrustLine() Expect(tl.Limit).To(Equal(xdr.Int64(40000000000))) }) It("properly returns the state of a trustlines that gets removed", func() { before, err := updateTrustline.StateBefore(tlkey, 0) Expect(err).ToNot(HaveOccurred()) Expect(before).ToNot(BeNil())
func (is *Session) ingestEffects() { if is.Err != nil { return } effects := &EffectIngestion{ Dest: is.Ingestion, Accounts: is.accountCache, OperationID: is.Cursor.OperationID(), } source := is.Cursor.OperationSourceAccount() opbody := is.Cursor.Operation().Body switch is.Cursor.OperationType() { case xdr.OperationTypeCreateAccount: op := opbody.MustCreateAccountOp() effects.Add(op.Destination, history.EffectAccountCreated, map[string]interface{}{ "starting_balance": amount.String(op.StartingBalance), }, ) effects.Add(source, history.EffectAccountDebited, map[string]interface{}{ "asset_type": "native", "amount": amount.String(op.StartingBalance), }, ) effects.Add(op.Destination, history.EffectSignerCreated, map[string]interface{}{ "public_key": op.Destination.Address(), "weight": keypair.DefaultSignerWeight, }, ) case xdr.OperationTypePayment: op := opbody.MustPaymentOp() dets := map[string]interface{}{"amount": amount.String(op.Amount)} is.assetDetails(dets, op.Asset, "") effects.Add(op.Destination, history.EffectAccountCredited, dets) effects.Add(source, history.EffectAccountDebited, dets) case xdr.OperationTypePathPayment: op := opbody.MustPathPaymentOp() dets := map[string]interface{}{"amount": amount.String(op.DestAmount)} is.assetDetails(dets, op.DestAsset, "") effects.Add(op.Destination, history.EffectAccountCredited, dets) result := is.Cursor.OperationResult().MustPathPaymentResult() dets = map[string]interface{}{"amount": amount.String(result.SendAmount())} is.assetDetails(dets, op.SendAsset, "") effects.Add(source, history.EffectAccountDebited, dets) is.ingestTrades(effects, source, result.MustSuccess().Offers) case xdr.OperationTypeManageOffer: result := is.Cursor.OperationResult().MustManageOfferResult().MustSuccess() is.ingestTrades(effects, source, result.OffersClaimed) case xdr.OperationTypeCreatePassiveOffer: claims := []xdr.ClaimOfferAtom{} result := is.Cursor.OperationResult() // KNOWN ISSUE: stellar-core creates results for CreatePassiveOffer operations // with the wrong result arm set. if result.Type == xdr.OperationTypeManageOffer { claims = result.MustManageOfferResult().MustSuccess().OffersClaimed } else { claims = result.MustCreatePassiveOfferResult().MustSuccess().OffersClaimed } is.ingestTrades(effects, source, claims) case xdr.OperationTypeSetOptions: op := opbody.MustSetOptionsOp() if op.HomeDomain != nil { effects.Add(source, history.EffectAccountHomeDomainUpdated, map[string]interface{}{ "home_domain": string(*op.HomeDomain), }, ) } thresholdDetails := map[string]interface{}{} if op.LowThreshold != nil { thresholdDetails["low_threshold"] = *op.LowThreshold } if op.MedThreshold != nil { thresholdDetails["med_threshold"] = *op.MedThreshold } if op.HighThreshold != nil { thresholdDetails["high_threshold"] = *op.HighThreshold } if len(thresholdDetails) > 0 { effects.Add(source, history.EffectAccountThresholdsUpdated, thresholdDetails) } flagDetails := map[string]bool{} is.effectFlagDetails(flagDetails, op.SetFlags, true) is.effectFlagDetails(flagDetails, op.ClearFlags, false) if len(flagDetails) > 0 { effects.Add(source, history.EffectAccountFlagsUpdated, flagDetails) } is.ingestSignerEffects(effects, op) case xdr.OperationTypeChangeTrust: op := opbody.MustChangeTrustOp() dets := map[string]interface{}{"limit": amount.String(op.Limit)} key := xdr.LedgerKey{} effect := history.EffectType(0) is.assetDetails(dets, op.Line, "") key.SetTrustline(source, op.Line) before, after, err := is.Cursor.BeforeAndAfter(key) // NOTE: when an account trusts itself, the transaction is successful but // no ledger entries are actually modified, leading to an "empty meta" // situation. We simply continue on to the next operation in that scenario. if err == meta.ErrMetaNotFound { return } if err != nil { is.Err = err return } switch { case before == nil && after != nil: effect = history.EffectTrustlineCreated case before != nil && after == nil: effect = history.EffectTrustlineRemoved case before != nil && after != nil: effect = history.EffectTrustlineUpdated default: panic("Invalid before-and-after state") } effects.Add(source, effect, dets) case xdr.OperationTypeAllowTrust: op := opbody.MustAllowTrustOp() asset := op.Asset.ToAsset(source) dets := map[string]interface{}{ "trustor": op.Trustor.Address(), } is.assetDetails(dets, asset, "") if op.Authorize { effects.Add(source, history.EffectTrustlineAuthorized, dets) } else { effects.Add(source, history.EffectTrustlineDeauthorized, dets) } case xdr.OperationTypeAccountMerge: dest := opbody.MustDestination() result := is.Cursor.OperationResult().MustAccountMergeResult() dets := map[string]interface{}{ "amount": amount.String(result.MustSourceAccountBalance()), "asset_type": "native", } effects.Add(source, history.EffectAccountDebited, dets) effects.Add(dest, history.EffectAccountCredited, dets) effects.Add(source, history.EffectAccountRemoved, map[string]interface{}{}) case xdr.OperationTypeInflation: payouts := is.Cursor.OperationResult().MustInflationResult().MustPayouts() for _, payout := range payouts { effects.Add(payout.Destination, history.EffectAccountCredited, map[string]interface{}{ "amount": amount.String(payout.Amount), "asset_type": "native", }, ) } case xdr.OperationTypeManageData: op := opbody.MustManageDataOp() dets := map[string]interface{}{"name": op.DataName} key := xdr.LedgerKey{} effect := history.EffectType(0) key.SetData(source, string(op.DataName)) before, after, err := is.Cursor.BeforeAndAfter(key) if err != nil { is.Err = err return } if after != nil { raw := after.Data.MustData().DataValue dets["value"] = base64.StdEncoding.EncodeToString(raw) } switch { case before == nil && after != nil: effect = history.EffectDataCreated case before != nil && after == nil: effect = history.EffectDataRemoved case before != nil && after != nil: effect = history.EffectDataUpdated default: panic("Invalid before-and-after state") } effects.Add(source, effect, dets) default: is.Err = fmt.Errorf("Unknown operation type: %s", is.Cursor.OperationType()) return } is.Err = effects.Finish() }