func (x *XdrStream) ReadOne(in interface{}) error { var nbytes uint32 err := binary.Read(x.rdr, binary.BigEndian, &nbytes) if err != nil { x.rdr.Close() if err == io.ErrUnexpectedEOF { return io.EOF } else { return err } } nbytes &= 0x7fffffff x.buf.Reset() if nbytes == 0 { x.rdr.Close() return io.EOF } x.buf.Grow(int(nbytes)) read, err := x.buf.ReadFrom(io.LimitReader(x.rdr, int64(nbytes))) if read != int64(nbytes) { x.rdr.Close() return errors.New("Read wrong number of bytes from XDR") } if err != nil { x.rdr.Close() return err } readi, err := xdr.Unmarshal(&x.buf, in) if int64(readi) != int64(nbytes) { return fmt.Errorf("Unmarshalled %d bytes from XDR, expected %d)", readi, nbytes) } return err }
func (ac CoreAccountRecord) DecodeThresholds() (xdr.Thresholds, error) { reader := strings.NewReader(ac.Thresholds) b64r := base64.NewDecoder(base64.StdEncoding, reader) var xdrThresholds xdr.Thresholds _, err := xdr.Unmarshal(b64r, &xdrThresholds) if err != nil { err = errors.Wrap(err, 1) } return xdrThresholds, err }
func ExampleDecodeTransaction() { data := "AAAAAGL8HQvQkbK2HA3WVjRrKmjX00fG8sLI7m0ERwJW/AX3AAAACgAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAABVvwF9wAAAEAKZ7IPj/46PuWU6ZOtyMosctNAkXRNX9WCAI5RnfRk+AyxDLoDZP/9l3NvsxQtWj9juQOuoBlFLnWu8intgxQA" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var tx xdr.TransactionEnvelope bytesRead, err := xdr.Unmarshal(b64r, &tx) fmt.Printf("read %d bytes\n", bytesRead) if err != nil { log.Fatal(err) } fmt.Printf("This tx has %d operations\n", len(tx.Tx.Operations)) // Output: read 192 bytes // This tx has 1 operations }
func ExampleDecodeTransaction() { data := "AAAAAImbKEDtVjbFbdxfFLI5dfefG6I4jSaU5MVuzd3JYOXvAAAACgAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAAByWDl7wAAAEAza1ouO/OTJDMMwUDewoqooFqHDulZ/nWFekNycPVCRtw70wZIN0UURhx8lYh1e911oahT3nBjAFZgwAwijY0O" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var tx xdr.TransactionEnvelope bytesRead, err := xdr.Unmarshal(b64r, &tx) fmt.Printf("read %d bytes\n", bytesRead) if err != nil { log.Fatal(err) } fmt.Printf("This tx has %d operations\n", len(tx.Tx.Operations)) // Output: read 192 bytes // This tx has 1 operations }
func ExampleDecodeTransaction() { data := "rqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAKAAAAAwAAAAEAAAAAAAA" + "AAAAAAAEAAAAAAAAAAW5oJtVdnYOVdZqtXpTHBtbcY0mCmfcBIKEgWnlvFIhaAAAAAA" + "AAAAAC+vCAAAAAAa6jei0gQGmrUfm+o2CMv/w32YzJgGYlmgG6CUW3FwyD6AZ/5TtPZ" + "qEt9kyC3GJeXfzoS667ZPhPUSNjSWgAeDPHFLcM" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var tx xdr.TransactionEnvelope bytesRead, err := xdr.Unmarshal(b64r, &tx) fmt.Printf("read %d bytes\n", bytesRead) if err != nil { log.Fatal(err) } fmt.Printf("This tx has %d operations\n", len(tx.Tx.Operations)) // Output: read 180 bytes // This tx has 1 operations }
func TestDecode(t *testing.T) { Convey("Decode XDR Enum", t, func() { // works for positive values var op xdr.OperationType r := bytes.NewReader([]byte{0x00, 0x00, 0x00, 0x00}) xdr.Unmarshal(r, &op) So(op, ShouldEqual, xdr.OperationTypeCreateAccount) r = bytes.NewReader([]byte{0x00, 0x00, 0x00, 0x08}) xdr.Unmarshal(r, &op) So(op, ShouldEqual, xdr.OperationTypeAccountMerge) // works for negative values var trc xdr.TransactionResultCode r = bytes.NewReader([]byte{0xFF, 0xFF, 0xFF, 0xFF}) xdr.Unmarshal(r, &trc) So(trc, ShouldEqual, xdr.TransactionResultCodeTxFailed) r = bytes.NewReader([]byte{0x00, 0xFF, 0xFF, 0xFF}) _, err := xdr.Unmarshal(r, &op) So(err, ShouldNotBeNil) }) Convey("Decodes Memo", t, func() { data := "AAAAAA==" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var memo xdr.Memo _, err := xdr.Unmarshal(b64r, &memo) So(err, ShouldBeNil) }) Convey("Decodes AccountID", t, func() { data := "AAAAAK6jei3jmoI8TGlD/egc37PXtHKKzWV8wViZBaCu5L5M" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var id xdr.AccountId _, err := xdr.Unmarshal(b64r, &id) So(err, ShouldBeNil) }) Convey("Decodes OperationBody", t, func() { data := "AAAAAAAAAACuo3ot45qCPExpQ/3oHN+z17Ryis1lfMFYmQWgruS+TAAAAAA7msoA" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var body xdr.OperationBody _, err := xdr.Unmarshal(b64r, &body) So(err, ShouldBeNil) }) Convey("Decode TransactionResultPair", t, func() { data := "mf13Xm7tPjMcffhLVA2VXbTs6fV9IpgHFZGKy3zlu/QAAAAAAAAACgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA==" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var trp xdr.TransactionResultPair _, err := xdr.Unmarshal(b64r, &trp) So(err, ShouldBeNil) So(len(trp.TransactionHash), ShouldEqual, 32) So(trp.Result.FeeCharged, ShouldEqual, 10) trr := trp.Result.Result So(trr.Code, ShouldEqual, xdr.TransactionResultCodeTxSuccess) So(trr.Results, ShouldNotBeNil) r := trr.MustResults() So(len(r), ShouldEqual, 1) opr := r[0] So(opr.Code, ShouldEqual, xdr.OperationResultCodeOpInner) oprr := opr.MustTr() So(oprr.Type, ShouldEqual, xdr.OperationTypeCreateAccount) cr := oprr.MustCreateAccountResult() So(cr.Code, ShouldEqual, xdr.CreateAccountResultCodeCreateAccountSuccess) So(func() { oprr.MustAccountMergeResult() }, ShouldPanic) }) Convey("Decode TransactionEnvelope", t, func() { data := "AAAAAGL8HQvQkbK2HA3WVjRrKmjX00fG8sLI7m0ERwJW/AX3AAAACgAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAABVvwF9wAAAEAKZ7IPj/46PuWU6ZOtyMosctNAkXRNX9WCAI5RnfRk+AyxDLoDZP/9l3NvsxQtWj9juQOuoBlFLnWu8intgxQA" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var txe xdr.TransactionEnvelope _, err := xdr.Unmarshal(b64r, &txe) So(err, ShouldBeNil) So(len(txe.Signatures), ShouldEqual, 1) tx := txe.Tx So(tx.Fee, ShouldEqual, 10) So(tx.SeqNum, ShouldEqual, 1) So(tx.Memo.Type, ShouldEqual, xdr.MemoTypeMemoNone) op := tx.Operations[0] So(op.SourceAccount, ShouldBeNil) p := op.Body.MustCreateAccountOp() So(p.StartingBalance, ShouldEqual, 1000000000) }) Convey("Decode TransactionMeta", t, func() { data := "AAAAAAAAAAEAAAABAAAAAgAAAAAAAAAAYvwdC9CRsrYcDdZWNGsqaNfTR8bywsjubQRHAlb8BfcBY0V4XYn/9gAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAAAAAACAAAAAAAAAACuo3ot45qCPExpQ/3oHN+z17Ryis1lfMFYmQWgruS+TAAAAAA7msoAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAAAAAABi/B0L0JGythwN1lY0aypo19NHxvLCyO5tBEcCVvwF9wFjRXgh7zX2AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var m xdr.TransactionMeta _, err := xdr.Unmarshal(b64r, &m) So(err, ShouldBeNil) op := m.MustOperations() So(len(op), ShouldEqual, 1) So(len(op[0].Changes), ShouldEqual, 1) }) Convey("Roundtrip TransactionEnvelope", t, func() { data := "AAAAAGL8HQvQkbK2HA3WVjRrKmjX00fG8sLI7m0ERwJW/AX3AAAACgAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAABVvwF9wAAAEAKZ7IPj/46PuWU6ZOtyMosctNAkXRNX9WCAI5RnfRk+AyxDLoDZP/9l3NvsxQtWj9juQOuoBlFLnWu8intgxQA" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var txe xdr.TransactionEnvelope n, err := xdr.Unmarshal(b64r, &txe) So(err, ShouldBeNil) var output bytes.Buffer b64w := base64.NewEncoder(base64.StdEncoding, &output) n2, err := xdr.Marshal(b64w, txe) So(n2, ShouldEqual, n) So(output.String(), ShouldEqual, data) }) }
// 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) }
func unmarshalTransactionResult(transactionResult string) (txResult xdr.TransactionResult, err error) { reader := strings.NewReader(transactionResult) b64r := base64.NewDecoder(base64.StdEncoding, reader) _, err = xdr.Unmarshal(b64r, &txResult) return }
func TestDecode(t *testing.T) { Convey("Decode XDR Enum", t, func() { // works for positive values var op xdr.OperationType r := bytes.NewReader([]byte{0x00, 0x00, 0x00, 0x00}) xdr.Unmarshal(r, &op) So(op, ShouldEqual, xdr.OperationTypeCreateAccount) r = bytes.NewReader([]byte{0x00, 0x00, 0x00, 0x08}) xdr.Unmarshal(r, &op) So(op, ShouldEqual, xdr.OperationTypeAccountMerge) // works for negative values var trc xdr.TransactionResultCode r = bytes.NewReader([]byte{0xFF, 0xFF, 0xFF, 0xFF}) xdr.Unmarshal(r, &trc) So(trc, ShouldEqual, xdr.TransactionResultCodeTxFailed) r = bytes.NewReader([]byte{0x00, 0xFF, 0xFF, 0xFF}) _, err := xdr.Unmarshal(r, &op) So(err, ShouldNotBeNil) }) Convey("Decode TransactionResultPair", t, func() { data := "W9EizvB5Q+UMEAJR9w3y+/xvR14aO27zXb/yoQod9L8AAAAAAAAACgAAAAAAAAABAAAAAAAAAAEAAAAA" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var trp xdr.TransactionResultPair _, err := xdr.Unmarshal(b64r, &trp) So(err, ShouldBeNil) So(len(trp.TransactionHash), ShouldEqual, 32) So(trp.Result.FeeCharged, ShouldEqual, 10) trr := trp.Result.Result So(trr.Code, ShouldEqual, xdr.TransactionResultCodeTxSuccess) So(trr.Results, ShouldNotBeNil) r := trr.MustResults() So(len(r), ShouldEqual, 1) opr := r[0] So(opr.Code, ShouldEqual, xdr.OperationResultCodeOpInner) oprr := opr.MustTr() So(oprr.Type, ShouldEqual, xdr.OperationTypePayment) pr := oprr.MustPaymentResult() So(pr.Code, ShouldEqual, xdr.PaymentResultCodePaymentSuccess) So(func() { oprr.MustAccountMergeResult() }, ShouldPanic) }) Convey("Decode TransactionEnvelope", t, func() { data := "rqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAKAAAAAwAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAW5oJtVdnYOVdZqtXpTHBtbcY0mCmfcBIKEgWnlvFIhaAAAAAAAAAAAC+vCAAAAAAa6jei0gQGmrUfm+o2CMv/w32YzJgGYlmgG6CUW3FwyD6AZ/5TtPZqEt9kyC3GJeXfzoS667ZPhPUSNjSWgAeDPHFLcM" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var txe xdr.TransactionEnvelope _, err := xdr.Unmarshal(b64r, &txe) So(err, ShouldBeNil) So(len(txe.Signatures), ShouldEqual, 1) tx := txe.Tx So(tx.Fee, ShouldEqual, 10) So(tx.SeqNum, ShouldEqual, 12884901889) So(tx.Memo.Type, ShouldEqual, xdr.MemoTypeMemoNone) op := tx.Operations[0] So(op.SourceAccount, ShouldBeNil) p := op.Body.MustPaymentOp() So(p.Currency.Type, ShouldEqual, xdr.CurrencyTypeCurrencyTypeNative) So(p.Amount, ShouldEqual, 50000000) }) Convey("Decode TransactionMeta", t, func() { data := "AAAAAgAAAAEAAAAAbmgm1V2dg5V1mq1elMcG1txjSYKZ9wEgoSBaeW8UiFoAAAAAPpW6gAAAAAMAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAQAAAACuo3ot45qCPExpQ/3oHN+z17Ryis1lfMFYmQWgruS+TAAAAAA4n9l2AAAAAwAAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAA=" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var m xdr.TransactionMeta _, err := xdr.Unmarshal(b64r, &m) So(err, ShouldBeNil) So(len(m.Changes), ShouldEqual, 2) m.Changes[0].MustUpdated() m.Changes[1].MustUpdated() }) Convey("Roundtrip TransactionEnvelope", t, func() { data := "rqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAKAAAAAwAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAW5oJtVdnYOVdZqtXpTHBtbcY0mCmfcBIKEgWnlvFIhaAAAAAAAAAAAC+vCAAAAAAa6jei0gQGmrUfm+o2CMv/w32YzJgGYlmgG6CUW3FwyD6AZ/5TtPZqEt9kyC3GJeXfzoS667ZPhPUSNjSWgAeDPHFLcM" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var txe xdr.TransactionEnvelope n, err := xdr.Unmarshal(b64r, &txe) So(err, ShouldBeNil) var output bytes.Buffer b64w := base64.NewEncoder(base64.StdEncoding, &output) n2, err := xdr.Marshal(b64w, txe) So(n2, ShouldEqual, n) So(output.String(), ShouldEqual, data) }) }
// 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) }
func TestXdrDecode(t *testing.T) { xdrbytes := []byte{ 0, 0, 0, 0, // entry type 0, liveentry 0, 32, 223, 100, // lastmodified 2154340 0, 0, 0, 0, // entry type 0, account 0, 0, 0, 0, // key type 0 23, 140, 68, 253, // ed25519 key (32 bytes) 184, 162, 186, 195, 118, 239, 158, 210, 100, 241, 174, 254, 108, 110, 165, 140, 75, 76, 83, 141, 104, 212, 227, 80, 1, 214, 157, 7, 0, 0, 0, 29, // 64bit balance: 125339976000 46, 216, 65, 64, 0, 0, 129, 170, // 64bit seqnum: 142567144423475 0, 0, 0, 51, 0, 0, 0, 1, // numsubentries: 1 0, 0, 0, 1, // inflationdest type, populated 0, 0, 0, 0, // key type 0 87, 240, 19, 71, // ed25519 key (32 bytes) 52, 91, 9, 62, 213, 239, 178, 85, 161, 119, 108, 251, 168, 90, 76, 116, 12, 48, 134, 248, 115, 255, 117, 50, 19, 18, 170, 203, 0, 0, 0, 0, // flags 0, 0, 0, 19, // homedomain: 19 bytes + 1 null padding 99, 101, 110, 116, // "centaurus.xcoins.de" 97, 117, 114, 117, 115, 46, 120, 99, 111, 105, 110, 115, 46, 100, 101, 0, 1, 0, 0, 0, // thresholds 0, 0, 0, 0, // signers (null) 0, 0, 0, 0, // entry.account.ext.v: 0 0, 0, 0, 0, // entry.ext.v: 0 } assert.Equal(t, len(xdrbytes), 152) var tmp xdr.BucketEntry n, err := xdr.Unmarshal(bytes.NewReader(xdrbytes[:]), &tmp) fmt.Printf("Decoded %d bytes\n", n) if err != nil { panic(err) } assert.Equal(t, len(xdrbytes), n) var out bytes.Buffer n, err = xdr.Marshal(&out, &tmp) fmt.Printf("Encoded %d bytes\n", n) if err != nil { panic(err) } assert.Equal(t, out.Len(), n) assert.Equal(t, out.Bytes(), xdrbytes) }
func TestDecode(t *testing.T) { Convey("Decode XDR Enum", t, func() { // works for positive values var op xdr.OperationType r := bytes.NewReader([]byte{0x00, 0x00, 0x00, 0x00}) xdr.Unmarshal(r, &op) So(op, ShouldEqual, xdr.OperationTypeCreateAccount) r = bytes.NewReader([]byte{0x00, 0x00, 0x00, 0x08}) xdr.Unmarshal(r, &op) So(op, ShouldEqual, xdr.OperationTypeAccountMerge) // works for negative values var trc xdr.TransactionResultCode r = bytes.NewReader([]byte{0xFF, 0xFF, 0xFF, 0xFF}) xdr.Unmarshal(r, &trc) So(trc, ShouldEqual, xdr.TransactionResultCodeTxFailed) r = bytes.NewReader([]byte{0x00, 0xFF, 0xFF, 0xFF}) _, err := xdr.Unmarshal(r, &op) So(err, ShouldNotBeNil) }) Convey("Decodes Memo", t, func() { data := "AAAAAA==" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var memo xdr.Memo _, err := xdr.Unmarshal(b64r, &memo) So(err, ShouldBeNil) }) Convey("Decodes AccountID", t, func() { data := "AAAAAK6jei3jmoI8TGlD/egc37PXtHKKzWV8wViZBaCu5L5M" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var id xdr.AccountId _, err := xdr.Unmarshal(b64r, &id) So(err, ShouldBeNil) }) Convey("Decodes OperationBody", t, func() { data := "AAAAAAAAAACuo3ot45qCPExpQ/3oHN+z17Ryis1lfMFYmQWgruS+TAAAAAA7msoA" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var body xdr.OperationBody _, err := xdr.Unmarshal(b64r, &body) So(err, ShouldBeNil) }) Convey("Decode TransactionResultPair", t, func() { data := "mf13Xm7tPjMcffhLVA2VXbTs6fV9IpgHFZGKy3zlu/QAAAAAAAAACgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA==" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var trp xdr.TransactionResultPair _, err := xdr.Unmarshal(b64r, &trp) So(err, ShouldBeNil) So(len(trp.TransactionHash), ShouldEqual, 32) So(trp.Result.FeeCharged, ShouldEqual, 10) trr := trp.Result.Result So(trr.Code, ShouldEqual, xdr.TransactionResultCodeTxSuccess) So(trr.Results, ShouldNotBeNil) r := trr.MustResults() So(len(r), ShouldEqual, 1) opr := r[0] So(opr.Code, ShouldEqual, xdr.OperationResultCodeOpInner) oprr := opr.MustTr() So(oprr.Type, ShouldEqual, xdr.OperationTypeCreateAccount) cr := oprr.MustCreateAccountResult() So(cr.Code, ShouldEqual, xdr.CreateAccountResultCodeCreateAccountSuccess) So(func() { oprr.MustAccountMergeResult() }, ShouldPanic) }) Convey("Decode TransactionEnvelope", t, func() { data := "AAAAAImbKEDtVjbFbdxfFLI5dfefG6I4jSaU5MVuzd3JYOXvAAAACgAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAAByWDl7wAAAEAza1ouO/OTJDMMwUDewoqooFqHDulZ/nWFekNycPVCRtw70wZIN0UURhx8lYh1e911oahT3nBjAFZgwAwijY0O" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var txe xdr.TransactionEnvelope _, err := xdr.Unmarshal(b64r, &txe) So(err, ShouldBeNil) So(len(txe.Signatures), ShouldEqual, 1) tx := txe.Tx So(tx.Fee, ShouldEqual, 10) So(tx.SeqNum, ShouldEqual, 1) So(tx.Memo.Type, ShouldEqual, xdr.MemoTypeMemoNone) op := tx.Operations[0] So(op.SourceAccount, ShouldBeNil) p := op.Body.MustCreateAccountOp() So(p.StartingBalance, ShouldEqual, 1000000000) }) Convey("Decode TransactionMeta", t, func() { data := "AAAAAAAAAAEAAAABAAAAAAAAAACJmyhA7VY2xW3cXxSyOXX3nxuiOI0mlOTFbs3dyWDl7wFjRXhdif/2AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAQAAAAIAAAAAAAAAAAAAAACuo3ot45qCPExpQ/3oHN+z17Ryis1lfMFYmQWgruS+TAAAAAA7msoAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAiZsoQO1WNsVt3F8Usjl1958bojiNJpTkxW7N3clg5e8BY0V4Ie819gAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAA==" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var m xdr.TransactionMeta _, err := xdr.Unmarshal(b64r, &m) So(err, ShouldBeNil) tm := m.MustV0() So(len(tm.Changes), ShouldEqual, 1) So(len(tm.Operations), ShouldEqual, 1) So(len(tm.Operations[0].Changes), ShouldEqual, 2) }) Convey("Roundtrip TransactionEnvelope", t, func() { data := "AAAAAImbKEDtVjbFbdxfFLI5dfefG6I4jSaU5MVuzd3JYOXvAAAACgAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAArqN6LeOagjxMaUP96Bzfs9e0corNZXzBWJkFoK7kvkwAAAAAO5rKAAAAAAAAAAAByWDl7wAAAEAza1ouO/OTJDMMwUDewoqooFqHDulZ/nWFekNycPVCRtw70wZIN0UURhx8lYh1e911oahT3nBjAFZgwAwijY0O" rawr := strings.NewReader(data) b64r := base64.NewDecoder(base64.StdEncoding, rawr) var txe xdr.TransactionEnvelope n, err := xdr.Unmarshal(b64r, &txe) So(err, ShouldBeNil) var output bytes.Buffer b64w := base64.NewEncoder(base64.StdEncoding, &output) n2, err := xdr.Marshal(b64w, txe) So(n2, ShouldEqual, n) So(output.String(), ShouldEqual, data) }) }