// Validate validates if request fields are valid. Useful when checking if a request is correct. func (request *SendRequest) Validate() error { err := request.FormRequest.CheckRequired(request) if err != nil { return err } _, err = keypair.Parse(request.Source) if err != nil { return protocols.NewInvalidParameterError("source", request.Source) } if !validateStellarAddress(request.Sender) { return protocols.NewInvalidParameterError("sender", request.Sender) } if !validateStellarAddress(request.Destination) { return protocols.NewInvalidParameterError("destination", request.Destination) } _, err = keypair.Parse(request.AssetIssuer) if err != nil { return protocols.NewInvalidParameterError("asset_issuer", request.AssetIssuer) } return nil }
// Validate validates if request fields are valid. Useful when checking if a request is correct. func (request *AuthorizeRequest) Validate(allowedAssets []config.Asset, issuingAccountID string) error { err := request.FormRequest.CheckRequired(request) if err != nil { return err } _, err = keypair.Parse(request.AccountID) if err != nil { return protocols.NewInvalidParameterError("account_id", request.AccountID) } // Is asset allowed? allowed := false for _, asset := range allowedAssets { if asset.Code == request.AssetCode && asset.Issuer == issuingAccountID { allowed = true break } } if !allowed { return protocols.NewInvalidParameterError("asset_code", request.AssetCode) } return nil }
// Validate validates if request fields are valid. Useful when checking if a request is correct. func (request *PaymentRequest) Validate() error { err := request.FormRequest.CheckRequired(request) if err != nil { return err } if request.Source != "" { _, err = keypair.Parse(request.Source) if err != nil { return protocols.NewInvalidParameterError("source", request.Source) } } // Memo if request.MemoType == "" && request.Memo != "" { return protocols.NewMissingParameter("memo_type") } if request.MemoType != "" && request.Memo == "" { return protocols.NewMissingParameter("memo") } // Destination Asset if request.AssetCode == "" && request.AssetIssuer != "" { return protocols.NewMissingParameter("asset_code") } if request.AssetCode != "" && request.AssetIssuer == "" { return protocols.NewMissingParameter("asset_issuer") } if request.AssetIssuer != "" { _, err := keypair.Parse(request.AssetIssuer) if err != nil { return protocols.NewInvalidParameterError("asset_issuer", request.AssetIssuer) } } // Send Asset if request.SendAssetCode == "" && request.SendAssetIssuer != "" { return protocols.NewMissingParameter("send_asset_code") } if request.SendAssetCode != "" && request.SendAssetIssuer == "" { return protocols.NewMissingParameter("send_asset_issuer") } if request.SendAssetIssuer != "" { _, err := keypair.Parse(request.SendAssetIssuer) if err != nil { return protocols.NewInvalidParameterError("send_asset_issuer", request.SendAssetIssuer) } } return nil }
// Validate validates if operation body is valid. func (op AccountMergeOperationBody) Validate() error { if !protocols.IsValidAccountID(op.Destination) { return protocols.NewInvalidParameterError("destination", op.Destination) } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op ManageOfferOperationBody) Validate() error { if op.OfferID != nil { _, err := strconv.ParseUint(*op.OfferID, 10, 64) if err != nil { return protocols.NewInvalidParameterError("offer_id", *op.OfferID) } } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op CreateAccountOperationBody) Validate() error { if !protocols.IsValidAccountID(op.Destination) { return protocols.NewInvalidParameterError("destination", op.Destination) } if !protocols.IsValidAmount(op.StartingBalance) { return protocols.NewInvalidParameterError("starting_balance", op.StartingBalance) } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op AllowTrustOperationBody) Validate() error { if !protocols.IsValidAssetCode(op.AssetCode) { return protocols.NewInvalidParameterError("asset_code", op.AssetCode) } if !protocols.IsValidAccountID(op.Trustor) { return protocols.NewInvalidParameterError("trustor", op.Trustor) } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op ManageDataOperationBody) Validate() error { if len(op.Name) > 64 { return protocols.NewInvalidParameterError("name", op.Name) } data, err := base64.StdEncoding.DecodeString(op.Data) if err != nil || len(data) > 64 { return protocols.NewInvalidParameterError("data", op.Data) } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op InflationOperationBody) Validate() error { if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op SetOptionsOperationBody) Validate() error { if op.InflationDest != nil && !protocols.IsValidAccountID(*op.InflationDest) { return protocols.NewInvalidParameterError("inflation_dest", *op.InflationDest) } if op.Signer != nil { if !protocols.IsValidAccountID(op.Signer.PublicKey) { return protocols.NewInvalidParameterError("signer.public_key", op.Signer.PublicKey) } } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op ChangeTrustOperationBody) Validate() error { if !op.Asset.Validate() { return protocols.NewInvalidParameterError("asset", op.Asset.String()) } if op.Limit != nil { if !protocols.IsValidAmount(*op.Limit) { return protocols.NewInvalidParameterError("limit", *op.Limit) } } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if operation body is valid. func (op PathPaymentOperationBody) Validate() error { if !protocols.IsValidAccountID(op.Destination) { return protocols.NewInvalidParameterError("destination", op.Destination) } if !protocols.IsValidAmount(op.SendMax) { return protocols.NewInvalidParameterError("send_max", op.SendMax) } if !protocols.IsValidAmount(op.DestinationAmount) { return protocols.NewInvalidParameterError("destination_amount", op.DestinationAmount) } if !op.SendAsset.Validate() { return protocols.NewInvalidParameterError("send_asset", op.SendAsset.String()) } if !op.DestinationAsset.Validate() { return protocols.NewInvalidParameterError("destination_asset", op.DestinationAsset.String()) } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } for i, asset := range op.Path { if !asset.Validate() { return protocols.NewInvalidParameterError("path["+strconv.Itoa(i)+"]", asset.String()) } } return nil }
// Validate validates if operation body is valid. func (op PaymentOperationBody) Validate() error { if !protocols.IsValidAccountID(op.Destination) { return protocols.NewInvalidParameterError("destination", op.Destination) } if !protocols.IsValidAmount(op.Amount) { return protocols.NewInvalidParameterError("amount", op.Amount) } if !op.Asset.Validate() { return protocols.NewInvalidParameterError("asset", op.Asset.String()) } if op.Source != nil && !protocols.IsValidAccountID(*op.Source) { return protocols.NewInvalidParameterError("source", *op.Source) } return nil }
// Validate validates if the request is correct. func (r BuilderRequest) Validate() error { if !protocols.IsValidAccountID(r.Source) { return protocols.NewInvalidParameterError("source", r.Source) } for i, signer := range r.Signers { if !protocols.IsValidAccountID(signer) { return protocols.NewInvalidParameterError("signers["+strconv.Itoa(i)+"]", signer) } } for _, operation := range r.Operations { err := operation.Body.Validate() if err != nil { return err } } return nil }
// Builder implements /builder endpoint func (rh *RequestHandler) Builder(w http.ResponseWriter, r *http.Request) { var request bridge.BuilderRequest decoder := json.NewDecoder(r.Body) err := decoder.Decode(&request) if err != nil { log.WithFields(log.Fields{"err": err}).Error("Error decoding request") server.Write(w, protocols.InvalidParameterError) return } err = request.Process() if err != nil { errorResponse := err.(*protocols.ErrorResponse) log.WithFields(errorResponse.LogData).Error(errorResponse.Error()) server.Write(w, errorResponse) return } err = request.Validate() if err != nil { errorResponse := err.(*protocols.ErrorResponse) log.WithFields(errorResponse.LogData).Error(errorResponse.Error()) server.Write(w, errorResponse) return } sequenceNumber, err := strconv.ParseUint(request.SequenceNumber, 10, 64) if err != nil { errorResponse := protocols.NewInvalidParameterError("sequence_number", request.SequenceNumber) log.WithFields(errorResponse.LogData).Error(errorResponse.Error()) server.Write(w, errorResponse) return } mutators := []b.TransactionMutator{ b.SourceAccount{request.Source}, b.Sequence{sequenceNumber}, b.Network{rh.Config.NetworkPassphrase}, } for _, operation := range request.Operations { mutators = append(mutators, operation.Body.ToTransactionMutator()) } tx := b.Transaction(mutators...) if tx.Err != nil { log.WithFields(log.Fields{"err": err, "request": request}).Error("TransactionBuilder returned error") server.Write(w, protocols.InternalServerError) return } txe := tx.Sign(request.Signers...) txeB64, err := txe.Base64() if err != nil { log.WithFields(log.Fields{"err": err, "request": request}).Error("Error encoding transaction envelope") server.Write(w, protocols.InternalServerError) return } server.Write(w, &bridge.BuilderResponse{TransactionEnvelope: txeB64}) }
// Payment implements /payment endpoint func (rh *RequestHandler) Payment(w http.ResponseWriter, r *http.Request) { request := &bridge.PaymentRequest{} request.FromRequest(r) err := request.Validate() if err != nil { errorResponse := err.(*protocols.ErrorResponse) log.WithFields(errorResponse.LogData).Error(errorResponse.Error()) server.Write(w, errorResponse) return } if request.Source == "" { request.Source = rh.Config.Accounts.BaseSeed } sourceKeypair, _ := keypair.Parse(request.Source) var submitResponse horizon.SubmitTransactionResponse var submitError error if request.ExtraMemo != "" && rh.Config.Compliance != "" { // Compliance server part sendRequest := request.ToComplianceSendRequest() resp, err := rh.Client.PostForm( rh.Config.Compliance+"/send", sendRequest.ToValues(), ) if err != nil { log.WithFields(log.Fields{"err": err}).Error("Error sending request to compliance server") server.Write(w, protocols.InternalServerError) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Error("Error reading compliance server response") server.Write(w, protocols.InternalServerError) return } if resp.StatusCode != 200 { log.WithFields(log.Fields{ "status": resp.StatusCode, "body": string(body), }).Error("Error response from compliance server") server.Write(w, protocols.InternalServerError) return } var complianceSendResponse compliance.SendResponse err = json.Unmarshal(body, &complianceSendResponse) if err != nil { log.Error("Error unmarshalling from compliance server") server.Write(w, protocols.InternalServerError) return } if complianceSendResponse.AuthResponse.InfoStatus == compliance.AuthStatusPending || complianceSendResponse.AuthResponse.TxStatus == compliance.AuthStatusPending { log.WithFields(log.Fields{"response": complianceSendResponse}).Info("Compliance response pending") server.Write(w, bridge.NewPaymentPendingError(complianceSendResponse.AuthResponse.Pending)) return } if complianceSendResponse.AuthResponse.InfoStatus == compliance.AuthStatusDenied || complianceSendResponse.AuthResponse.TxStatus == compliance.AuthStatusDenied { log.WithFields(log.Fields{"response": complianceSendResponse}).Info("Compliance response denied") server.Write(w, bridge.PaymentDenied) return } var tx xdr.Transaction err = xdr.SafeUnmarshalBase64(complianceSendResponse.TransactionXdr, &tx) if err != nil { log.Error("Error unmarshalling transaction returned by compliance server") server.Write(w, protocols.InternalServerError) return } submitResponse, submitError = rh.TransactionSubmitter.SignAndSubmitRawTransaction(request.Source, &tx) } else { // Payment without compliance server destinationObject, _, err := rh.FederationResolver.Resolve(request.Destination) if err != nil { log.WithFields(log.Fields{"destination": request.Destination, "err": err}).Print("Cannot resolve address") server.Write(w, bridge.PaymentCannotResolveDestination) return } _, err = keypair.Parse(destinationObject.AccountID) if err != nil { log.WithFields(log.Fields{"AccountId": destinationObject.AccountID}).Print("Invalid AccountId in destination") server.Write(w, protocols.NewInvalidParameterError("destination", request.Destination)) return } var payWithMutator *b.PayWithPath if request.SendMax != "" { // Path payment var sendAsset b.Asset if request.SendAssetCode == "" && request.SendAssetIssuer == "" { sendAsset = b.NativeAsset() } else { sendAsset = b.CreditAsset(request.SendAssetCode, request.SendAssetIssuer) } payWith := b.PayWith(sendAsset, request.SendMax) for i := 0; ; i++ { codeFieldName := fmt.Sprintf("path[%d][asset_code]", i) issuerFieldName := fmt.Sprintf("path[%d][asset_issuer]", i) // If the element does not exist in PostForm break the loop if _, exists := r.PostForm[codeFieldName]; !exists { break } code := r.PostFormValue(codeFieldName) issuer := r.PostFormValue(issuerFieldName) if code == "" && issuer == "" { payWith = payWith.Through(b.NativeAsset()) } else { payWith = payWith.Through(b.CreditAsset(code, issuer)) } } payWithMutator = &payWith } var operationBuilder interface{} if request.AssetCode != "" && request.AssetIssuer != "" { mutators := []interface{}{ b.Destination{destinationObject.AccountID}, b.CreditAmount{request.AssetCode, request.AssetIssuer, request.Amount}, } if payWithMutator != nil { mutators = append(mutators, *payWithMutator) } operationBuilder = b.Payment(mutators...) } else { mutators := []interface{}{ b.Destination{destinationObject.AccountID}, b.NativeAmount{request.Amount}, } if payWithMutator != nil { mutators = append(mutators, *payWithMutator) } // Check if destination account exist _, err = rh.Horizon.LoadAccount(destinationObject.AccountID) if err != nil { log.WithFields(log.Fields{"error": err}).Error("Error loading account") operationBuilder = b.CreateAccount(mutators...) } else { operationBuilder = b.Payment(mutators...) } } memoType := request.MemoType memo := request.Memo if destinationObject.MemoType != "" { if request.MemoType != "" { log.Print("Memo given in request but federation returned memo fields.") server.Write(w, bridge.PaymentCannotUseMemo) return } memoType = destinationObject.MemoType memo = destinationObject.Memo } var memoMutator interface{} switch { case memoType == "": break case memoType == "id": id, err := strconv.ParseUint(memo, 10, 64) if err != nil { log.WithFields(log.Fields{"memo": memo}).Print("Cannot convert memo_id value to uint64") server.Write(w, protocols.NewInvalidParameterError("memo", request.Memo)) return } memoMutator = b.MemoID{id} case memoType == "text": memoMutator = &b.MemoText{memo} case memoType == "hash": memoBytes, err := hex.DecodeString(memo) if err != nil || len(memoBytes) != 32 { log.WithFields(log.Fields{"memo": memo}).Print("Cannot decode hash memo value") server.Write(w, protocols.NewInvalidParameterError("memo", request.Memo)) return } var b32 [32]byte copy(b32[:], memoBytes[0:32]) hash := xdr.Hash(b32) memoMutator = &b.MemoHash{hash} default: log.Print("Not supported memo type: ", memoType) server.Write(w, protocols.NewInvalidParameterError("memo", request.Memo)) return } accountResponse, err := rh.Horizon.LoadAccount(sourceKeypair.Address()) if err != nil { log.WithFields(log.Fields{"error": err}).Error("Cannot load source account") server.Write(w, bridge.PaymentSourceNotExist) return } sequenceNumber, err := strconv.ParseUint(accountResponse.SequenceNumber, 10, 64) if err != nil { log.WithFields(log.Fields{"error": err}).Error("Cannot convert SequenceNumber") server.Write(w, protocols.InternalServerError) return } transactionMutators := []b.TransactionMutator{ b.SourceAccount{request.Source}, b.Sequence{sequenceNumber + 1}, b.Network{rh.Config.NetworkPassphrase}, operationBuilder.(b.TransactionMutator), } if memoMutator != nil { transactionMutators = append(transactionMutators, memoMutator.(b.TransactionMutator)) } tx := b.Transaction(transactionMutators...) if tx.Err != nil { log.WithFields(log.Fields{"err": tx.Err}).Print("Transaction builder error") // TODO when build.OperationBuilder interface is ready check for // create_account and payment errors separately switch { case tx.Err.Error() == "Asset code length is invalid": server.Write( w, protocols.NewInvalidParameterError("asset_code", request.AssetCode), ) case strings.Contains(tx.Err.Error(), "cannot parse amount"): server.Write( w, protocols.NewInvalidParameterError("amount", request.Amount), ) default: log.WithFields(log.Fields{"err": tx.Err}).Print("Transaction builder error") server.Write(w, protocols.InternalServerError) } return } txe := tx.Sign(request.Source) txeB64, err := txe.Base64() if err != nil { log.WithFields(log.Fields{"error": err}).Error("Cannot encode transaction envelope") server.Write(w, protocols.InternalServerError) return } submitResponse, submitError = rh.Horizon.SubmitTransaction(txeB64) } if submitError != nil { log.WithFields(log.Fields{"error": submitError}).Error("Error submitting transaction") server.Write(w, protocols.InternalServerError) return } errorResponse := bridge.ErrorFromHorizonResponse(submitResponse) if errorResponse != nil { log.WithFields(errorResponse.LogData).Error(errorResponse.Error()) server.Write(w, errorResponse) return } // Path payment send amount if submitResponse.ResultXdr != nil { var transactionResult xdr.TransactionResult reader := strings.NewReader(*submitResponse.ResultXdr) b64r := base64.NewDecoder(base64.StdEncoding, reader) _, err := xdr.Unmarshal(b64r, &transactionResult) if err == nil && transactionResult.Result.Code == xdr.TransactionResultCodeTxSuccess { operationResult := (*transactionResult.Result.Results)[0] if operationResult.Tr.PathPaymentResult != nil { sendAmount := operationResult.Tr.PathPaymentResult.SendAmount() submitResponse.SendAmount = amount.String(sendAmount) } } } server.Write(w, &submitResponse) }
// HandlerAuth implements authorize endpoint func (rh *RequestHandler) HandlerAuth(c web.C, w http.ResponseWriter, r *http.Request) { authreq := &compliance.AuthRequest{} authreq.FromRequest(r) err := authreq.Validate() if err != nil { errorResponse := err.(*protocols.ErrorResponse) log.WithFields(errorResponse.LogData).Error(errorResponse.Error()) server.Write(w, errorResponse) return } var authData compliance.AuthData err = json.Unmarshal([]byte(authreq.Data), &authData) if err != nil { errorResponse := protocols.NewInvalidParameterError("data", authreq.Data) log.WithFields(errorResponse.LogData).Warn(errorResponse.Error()) server.Write(w, errorResponse) return } senderStellarToml, err := rh.StellarTomlResolver.GetStellarTomlByAddress(authData.Sender) if err != nil { log.WithFields(log.Fields{"err": err, "sender": authData.Sender}).Warn("Cannot get stellar.toml of sender") server.Write(w, protocols.InvalidParameterError) return } if senderStellarToml.SigningKey == "" { errorResponse := protocols.NewInvalidParameterError("data.sender", authData.Sender) log.WithFields(errorResponse.LogData).Warn("No SIGNING_KEY in stellar.toml of sender") server.Write(w, errorResponse) return } // Verify signature signatureBytes, err := base64.StdEncoding.DecodeString(authreq.Signature) if err != nil { errorResponse := protocols.NewInvalidParameterError("sig", authreq.Signature) log.WithFields(errorResponse.LogData).Warn("Error decoding signature") server.Write(w, errorResponse) return } err = rh.SignatureSignerVerifier.Verify(senderStellarToml.SigningKey, []byte(authreq.Data), signatureBytes) if err != nil { log.WithFields(log.Fields{ "signing_key": senderStellarToml.SigningKey, "data": authreq.Data, "sig": authreq.Signature, }).Warn("Invalid signature") errorResponse := protocols.NewInvalidParameterError("sig", authreq.Signature) server.Write(w, errorResponse) return } b64r := base64.NewDecoder(base64.StdEncoding, strings.NewReader(authData.Tx)) var tx xdr.Transaction _, err = xdr.Unmarshal(b64r, &tx) if err != nil { errorResponse := protocols.NewInvalidParameterError("data.tx", authData.Tx) log.WithFields(log.Fields{ "err": err, "tx": authData.Tx, }).Warn("Error decoding Transaction XDR") server.Write(w, errorResponse) return } if tx.Memo.Hash == nil { errorResponse := protocols.NewInvalidParameterError("data.tx", authData.Tx) log.WithFields(log.Fields{"tx": authData.Tx}).Warn("Transaction does not contain Memo.Hash") server.Write(w, errorResponse) return } // Validate memo preimage hash memoPreimageHashBytes := sha256.Sum256([]byte(authData.Memo)) memoBytes := [32]byte(*tx.Memo.Hash) if memoPreimageHashBytes != memoBytes { errorResponse := protocols.NewInvalidParameterError("data.tx", authData.Tx) h := xdr.Hash(memoPreimageHashBytes) tx.Memo.Hash = &h var txBytes bytes.Buffer _, err = xdr.Marshal(&txBytes, tx) if err != nil { log.Error("Error mashaling transaction") server.Write(w, protocols.InternalServerError) return } expectedTx := base64.StdEncoding.EncodeToString(txBytes.Bytes()) log.WithFields(log.Fields{"tx": authData.Tx, "expected_tx": expectedTx}).Warn("Memo preimage hash does not equal tx Memo.Hash") server.Write(w, errorResponse) return } var memoPreimage memo.Memo err = json.Unmarshal([]byte(authData.Memo), &memoPreimage) if err != nil { errorResponse := protocols.NewInvalidParameterError("data.memo", authData.Memo) log.WithFields(log.Fields{ "err": err, "memo": authData.Memo, }).Warn("Cannot unmarshal memo preimage") server.Write(w, errorResponse) return } transactionHash, err := submitter.TransactionHash(&tx, rh.Config.NetworkPassphrase) if err != nil { log.WithFields(log.Fields{"err": err}).Warn("Error calculating tx hash") server.Write(w, protocols.InternalServerError) return } response := compliance.AuthResponse{} // Sanctions check if rh.Config.Callbacks.Sanctions == "" { response.TxStatus = compliance.AuthStatusOk } else { resp, err := rh.Client.PostForm( rh.Config.Callbacks.Sanctions, url.Values{"sender": {memoPreimage.Transaction.SenderInfo}}, ) if err != nil { log.WithFields(log.Fields{ "sanctions": rh.Config.Callbacks.Sanctions, "err": err, }).Error("Error sending request to sanctions server") server.Write(w, protocols.InternalServerError) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Error("Error reading sanctions server response") server.Write(w, protocols.InternalServerError) return } switch resp.StatusCode { case http.StatusOK: // AuthStatusOk response.TxStatus = compliance.AuthStatusOk case http.StatusAccepted: // AuthStatusPending response.TxStatus = compliance.AuthStatusPending var pendingResponse compliance.PendingResponse err := json.Unmarshal(body, &pendingResponse) if err != nil { // Set default value response.Pending = 600 } else { response.Pending = pendingResponse.Pending } case http.StatusForbidden: // AuthStatusDenied response.TxStatus = compliance.AuthStatusDenied default: log.WithFields(log.Fields{ "status": resp.StatusCode, "body": string(body), }).Error("Error response from sanctions server") server.Write(w, protocols.InternalServerError) return } } // User info if authData.NeedInfo { if rh.Config.Callbacks.AskUser == "" { response.InfoStatus = compliance.AuthStatusDenied // Check AllowedFi tokens := strings.Split(authData.Sender, "*") if len(tokens) != 2 { log.WithFields(log.Fields{ "sender": authData.Sender, }).Warn("Invalid stellar address") server.Write(w, protocols.InternalServerError) return } allowedFi, err := rh.Repository.GetAllowedFiByDomain(tokens[1]) if err != nil { log.WithFields(log.Fields{"err": err}).Error("Error getting AllowedFi from DB") server.Write(w, protocols.InternalServerError) return } if allowedFi == nil { // FI not found check AllowedUser allowedUser, err := rh.Repository.GetAllowedUserByDomainAndUserID(tokens[1], tokens[0]) if err != nil { log.WithFields(log.Fields{"err": err}).Error("Error getting AllowedUser from DB") server.Write(w, protocols.InternalServerError) return } if allowedUser != nil { response.InfoStatus = compliance.AuthStatusOk } } else { response.InfoStatus = compliance.AuthStatusOk } } else { // Ask user var amount, assetType, assetCode, assetIssuer string if len(tx.Operations) > 0 { operationBody := tx.Operations[0].Body if operationBody.Type == xdr.OperationTypePayment { amount = baseAmount.String(operationBody.PaymentOp.Amount) operationBody.PaymentOp.Asset.Extract(&assetType, &assetCode, &assetIssuer) } else if operationBody.Type == xdr.OperationTypePathPayment { amount = baseAmount.String(operationBody.PathPaymentOp.DestAmount) operationBody.PathPaymentOp.DestAsset.Extract(&assetType, &assetCode, &assetIssuer) } } resp, err := rh.Client.PostForm( rh.Config.Callbacks.AskUser, url.Values{ "amount": {amount}, "asset_code": {assetCode}, "asset_issuer": {assetIssuer}, "sender": {memoPreimage.Transaction.SenderInfo}, "note": {memoPreimage.Transaction.Note}, }, ) if err != nil { log.WithFields(log.Fields{ "ask_user": rh.Config.Callbacks.AskUser, "err": err, }).Error("Error sending request to ask_user server") server.Write(w, protocols.InternalServerError) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Error("Error reading ask_user server response") server.Write(w, protocols.InternalServerError) return } switch resp.StatusCode { case http.StatusOK: // AuthStatusOk response.InfoStatus = compliance.AuthStatusOk case http.StatusAccepted: // AuthStatusPending response.InfoStatus = compliance.AuthStatusPending var pendingResponse compliance.PendingResponse err := json.Unmarshal(body, &pendingResponse) if err != nil { // Set default value response.Pending = 600 } else { response.Pending = pendingResponse.Pending } case http.StatusForbidden: // AuthStatusDenied response.InfoStatus = compliance.AuthStatusDenied default: log.WithFields(log.Fields{ "status": resp.StatusCode, "body": string(body), }).Error("Error response from ask_user server") server.Write(w, protocols.InternalServerError) return } } if response.InfoStatus == compliance.AuthStatusOk { // Fetch Info fetchInfoRequest := compliance.FetchInfoRequest{Address: memoPreimage.Transaction.Route} resp, err := rh.Client.PostForm( rh.Config.Callbacks.FetchInfo, fetchInfoRequest.ToValues(), ) if err != nil { log.WithFields(log.Fields{ "fetch_info": rh.Config.Callbacks.FetchInfo, "err": err, }).Error("Error sending request to fetch_info server") server.Write(w, protocols.InternalServerError) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.WithFields(log.Fields{ "fetch_info": rh.Config.Callbacks.FetchInfo, "err": err, }).Error("Error reading fetch_info server response") server.Write(w, protocols.InternalServerError) return } if resp.StatusCode != http.StatusOK { log.WithFields(log.Fields{ "fetch_info": rh.Config.Callbacks.FetchInfo, "status": resp.StatusCode, "body": string(body), }).Error("Error response from fetch_info server") server.Write(w, protocols.InternalServerError) return } response.DestInfo = string(body) } } else { response.InfoStatus = compliance.AuthStatusOk } if response.TxStatus == compliance.AuthStatusOk && response.InfoStatus == compliance.AuthStatusOk { authorizedTransaction := &entities.AuthorizedTransaction{ TransactionID: hex.EncodeToString(transactionHash[:]), Memo: base64.StdEncoding.EncodeToString(memoBytes[:]), TransactionXdr: authData.Tx, AuthorizedAt: time.Now(), Data: authreq.Data, } err = rh.EntityManager.Persist(authorizedTransaction) if err != nil { log.WithFields(log.Fields{"err": err}).Warn("Error persisting AuthorizedTransaction") server.Write(w, protocols.InternalServerError) return } } server.Write(w, &response) }
// Process parses operations and creates OperationBody object for each operation func (r BuilderRequest) Process() error { var err error for i, operation := range r.Operations { var operationBody OperationBody switch operation.Type { case OperationTypeCreateAccount: var createAccount CreateAccountOperationBody err = json.Unmarshal(operation.RawBody, &createAccount) operationBody = createAccount case OperationTypePayment: var payment PaymentOperationBody err = json.Unmarshal(operation.RawBody, &payment) operationBody = payment case OperationTypePathPayment: var pathPayment PathPaymentOperationBody err = json.Unmarshal(operation.RawBody, &pathPayment) operationBody = pathPayment case OperationTypeManageOffer: var manageOffer ManageOfferOperationBody err = json.Unmarshal(operation.RawBody, &manageOffer) operationBody = manageOffer case OperationTypeCreatePassiveOffer: var manageOffer ManageOfferOperationBody err = json.Unmarshal(operation.RawBody, &manageOffer) manageOffer.PassiveOffer = true operationBody = manageOffer case OperationTypeSetOptions: var setOptions SetOptionsOperationBody err = json.Unmarshal(operation.RawBody, &setOptions) operationBody = setOptions case OperationTypeChangeTrust: var changeTrust ChangeTrustOperationBody err = json.Unmarshal(operation.RawBody, &changeTrust) operationBody = changeTrust case OperationTypeAllowTrust: var allowTrust AllowTrustOperationBody err = json.Unmarshal(operation.RawBody, &allowTrust) operationBody = allowTrust case OperationTypeAccountMerge: var accountMerge AccountMergeOperationBody err = json.Unmarshal(operation.RawBody, &accountMerge) operationBody = accountMerge case OperationTypeInflation: var inflation InflationOperationBody err = json.Unmarshal(operation.RawBody, &inflation) operationBody = inflation case OperationTypeManageData: var manageData ManageDataOperationBody err = json.Unmarshal(operation.RawBody, &manageData) operationBody = manageData default: return protocols.NewInvalidParameterError("operations["+strconv.Itoa(i)+"][type]", string(operation.Type)) } if err != nil { return protocols.NewInvalidParameterError("operations["+strconv.Itoa(i)+"][body]", "", map[string]interface{}{"err": err}) } r.Operations[i].Body = operationBody } return nil }