// ExampleLowLevelTransaction creates and signs a simple transaction, and then // encodes it into a hex string capable of being submitted to stellar-core. // // It uses the low-level xdr facilities to create the transaction. func ExampleLowLevelTransaction() { spub, spriv, err := GenerateKeyFromSeed("s3Fy8h5LEcYVE8aofthKWHeJpygbntw5HgcekFw93K6XqTW4gEx") if err != nil { log.Fatal(err) } dpub, _, err := GenerateKeyFromSeed("sfkPCKA6XgaeHZH3NE57i3QrjVcw61c1noWQCgnHa6KJP2BrbXD") if err != nil { log.Fatal(err) } op := xdr.PaymentOp{ Destination: dpub.KeyData(), Currency: xdr.NewCurrencyCurrencyTypeNative(), Amount: 50 * 10000000, } tx := xdr.Transaction{ SourceAccount: spub.KeyData(), Fee: 10, SeqNum: xdr.SequenceNumber(1), Memo: xdr.NewMemoMemoNone(), Operations: []xdr.Operation{ {Body: xdr.NewOperationBodyPayment(op)}, }, } var txBytes bytes.Buffer _, err = xdr.Marshal(&txBytes, tx) if err != nil { log.Fatal(err) } txHash := Hash(txBytes.Bytes()) signature := spriv.Sign(txHash[:]) ds := xdr.DecoratedSignature{ Hint: spriv.Hint(), Signature: xdr.Uint512(signature), } txe := xdr.TransactionEnvelope{ Tx: tx, Signatures: []xdr.DecoratedSignature{ds}, } var txeBytes bytes.Buffer _, err = xdr.Marshal(&txeBytes, txe) if err != nil { log.Fatal(err) } txeHex := hex.EncodeToString(txeBytes.Bytes()) fmt.Printf("tx hex: %s", txeHex) // Output: tx hex: 3658fe7598d20c7a6de3297f84bc52bd2a1ec8c0f1f6c5b41cc1c7571b4331f00000000a000000000000000100000000000000000000000100000000000000012d24692ed08bbf679ba199448870d2191e876fecd92fdd9f6d274da4e6de134100000000000000001dcd650000000001dd302d0c0cee527cf02f6a0aec6916966298712914c63e3c57de74a6e27c29ea234a555fcc36533417afe4e1147815a42529fbca3429bc7caf0a06dc6b383ca6e9d4d80f }
func txResultFromCore(tx core.Transaction) txsub.Result { // re-encode result to base64 var raw bytes.Buffer _, err := xdr.Marshal(&raw, tx.Result.Result) if err != nil { return txsub.Result{Err: err} } trx := base64.StdEncoding.EncodeToString(raw.Bytes()) // if result is success, send a normal resposne if tx.Result.Result.Result.Code == xdr.TransactionResultCodeTxSuccess { return txsub.Result{ Hash: tx.TransactionHash, LedgerSequence: tx.LedgerSequence, EnvelopeXDR: tx.EnvelopeXDR(), ResultXDR: trx, ResultMetaXDR: tx.ResultMetaXDR(), } } // if failed, produce a FailedTransactionError return txsub.Result{ Err: &txsub.FailedTransactionError{ ResultXDR: trx, }, Hash: tx.TransactionHash, LedgerSequence: tx.LedgerSequence, EnvelopeXDR: tx.EnvelopeXDR(), ResultXDR: trx, ResultMetaXDR: tx.ResultMetaXDR(), } }
// Hash returns the hash of this builder's transaction. func (b *TransactionBuilder) Hash() ([32]byte, error) { var txBytes bytes.Buffer _, err := xdr.Marshal(&txBytes, b.TX) if err != nil { return [32]byte{}, err } return stellarbase.Hash(txBytes.Bytes()), nil }
func HashXdr(x interface{}) (Hash, error) { var msg bytes.Buffer _, err := xdr.Marshal(&msg, x) if err != nil { var zero Hash return zero, err } return Hash(sha256.Sum256(msg.Bytes())), nil }
// TransactionHash returns transaction hash for a given Transaction based on the network func TransactionHash(tx *xdr.Transaction, networkPassphrase string) ([32]byte, error) { var txBytes bytes.Buffer _, err := fmt.Fprintf(&txBytes, "%s", hash.Hash([]byte(networkPassphrase))) if err != nil { return [32]byte{}, err } _, err = xdr.Marshal(&txBytes, xdr.EnvelopeTypeEnvelopeTypeTx) if err != nil { return [32]byte{}, err } _, err = xdr.Marshal(&txBytes, tx) if err != nil { return [32]byte{}, err } return hash.Hash(txBytes.Bytes()), nil }
// Hash returns the hash of this builder's transaction. func (b *TransactionBuilder) Hash() ([32]byte, error) { var txBytes bytes.Buffer _, err := fmt.Fprintf(&txBytes, "%s", b.NetworkID) if err != nil { return [32]byte{}, err } _, err = xdr.Marshal(&txBytes, xdr.EnvelopeTypeEnvelopeTypeTx) if err != nil { return [32]byte{}, err } _, err = xdr.Marshal(&txBytes, b.TX) if err != nil { return [32]byte{}, err } return hash.Hash(txBytes.Bytes()), nil }
// Bytes encodes the builder's underlying envelope to XDR func (b TransactionEnvelopeBuilder) Bytes() ([]byte, error) { if b.Err != nil { return nil, b.Err } var txBytes bytes.Buffer _, err := xdr.Marshal(&txBytes, b.E) if err != nil { return nil, err } return txBytes.Bytes(), nil }
func WriteFramedXdr(out io.Writer, in interface{}) error { var tmp bytes.Buffer n, err := xdr.Marshal(&tmp, in) if n > 0x7fffffff { return fmt.Errorf("Overlong write: %d bytes", n) } nbytes := uint32(n | 0x80000000) binary.Write(out, binary.BigEndian, &nbytes) k, err := tmp.WriteTo(out) if int64(n) != k { return fmt.Errorf("Mismatched write length: %d vs. %d", n, k) } return err }
func txResultFromCore(tx CoreTransactionRecord) txsub.Result { //decode the result xdr, extract TransactionResult var trp xdr.TransactionResultPair err := xdr.SafeUnmarshalBase64(tx.ResultXDR, &trp) if err != nil { return txsub.Result{Err: err} } tr := trp.Result // re-encode result to base64 var raw bytes.Buffer _, err = xdr.Marshal(&raw, tr) if err != nil { return txsub.Result{Err: err} } trx := base64.StdEncoding.EncodeToString(raw.Bytes()) // if result is success, send a normal resposne if tr.Result.Code == xdr.TransactionResultCodeTxSuccess { return txsub.Result{ Hash: tx.TransactionHash, LedgerSequence: tx.LedgerSequence, EnvelopeXDR: tx.EnvelopeXDR, ResultXDR: trx, ResultMetaXDR: tx.ResultMetaXDR, } } // if failed, produce a FailedTransactionError return txsub.Result{ Err: &txsub.FailedTransactionError{ ResultXDR: trx, }, Hash: tx.TransactionHash, LedgerSequence: tx.LedgerSequence, EnvelopeXDR: tx.EnvelopeXDR, ResultXDR: trx, ResultMetaXDR: tx.ResultMetaXDR, } }
func HashTxSet(txset *xdr.TransactionSet) (Hash, error) { err := SortTxsForHash(txset) var h Hash if err != nil { return h, err } hsh := sha256.New() hsh.Write(txset.PreviousLedgerHash[:]) for _, env := range txset.Txs { _, err := xdr.Marshal(hsh, &env) if err != nil { return h, err } } sum := hsh.Sum([]byte{}) copy(h[:], sum[:]) return h, nil }
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) }) }
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) }
// HandlerSend implements /send endpoint func (rh *RequestHandler) HandlerSend(c web.C, w http.ResponseWriter, r *http.Request) { request := &compliance.SendRequest{} 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 } destinationObject, stellarToml, 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, compliance.CannotResolveDestination) return } if stellarToml.AuthServer == "" { log.Print("No AUTH_SERVER in stellar.toml") server.Write(w, compliance.AuthServerNotDefined) return } var payWithMutator *b.PayWithPath if request.SendMax != "" { // Path payment var sendAsset b.Asset if request.SendAssetCode != "" && request.SendAssetIssuer != "" { sendAsset = b.CreditAsset(request.SendAssetCode, request.SendAssetIssuer) } else if request.SendAssetCode == "" && request.SendAssetIssuer == "" { sendAsset = b.NativeAsset() } else { log.Print("Missing send asset param.") server.Write(w, protocols.MissingParameterError) return } payWith := b.PayWith(sendAsset, request.SendMax) for _, asset := range request.Path { if asset.Code == "" && asset.Issuer == "" { payWith = payWith.Through(b.NativeAsset()) } else { payWith = payWith.Through(b.CreditAsset(asset.Code, asset.Issuer)) } } payWithMutator = &payWith } mutators := []interface{}{ b.Destination{destinationObject.AccountID}, b.CreditAmount{ request.AssetCode, request.AssetIssuer, request.Amount, }, } if payWithMutator != nil { mutators = append(mutators, *payWithMutator) } operationMutator := b.Payment(mutators...) if operationMutator.Err != nil { log.WithFields(log.Fields{ "err": operationMutator.Err, }).Error("Error creating operation") server.Write(w, protocols.InternalServerError) return } // Fetch Sender Info senderInfo := "" if rh.Config.Callbacks.FetchInfo != "" { fetchInfoRequest := compliance.FetchInfoRequest{Address: request.Sender} 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 } senderInfo = string(body) } memoPreimage := &memo.Memo{ Transaction: memo.Transaction{ SenderInfo: senderInfo, Route: destinationObject.Memo, Extra: request.ExtraMemo, }, } memoJSON := memoPreimage.Marshal() memoHashBytes := sha256.Sum256(memoJSON) memoMutator := &b.MemoHash{xdr.Hash(memoHashBytes)} transaction, err := submitter.BuildTransaction( request.Source, rh.Config.NetworkPassphrase, operationMutator, memoMutator, ) var txBytes bytes.Buffer _, err = xdr.Marshal(&txBytes, transaction) if err != nil { log.Error("Error mashaling transaction") server.Write(w, protocols.InternalServerError) return } txBase64 := base64.StdEncoding.EncodeToString(txBytes.Bytes()) authData := compliance.AuthData{ Sender: request.Sender, NeedInfo: rh.Config.NeedsAuth, Tx: txBase64, Memo: string(memoJSON), } data, err := json.Marshal(authData) if err != nil { log.Error("Error mashaling authData") server.Write(w, protocols.InternalServerError) return } sig, err := rh.SignatureSignerVerifier.Sign(rh.Config.Keys.SigningSeed, data) if err != nil { log.Error("Error signing authData") server.Write(w, protocols.InternalServerError) return } authRequest := compliance.AuthRequest{ Data: string(data), Signature: sig, } resp, err := rh.Client.PostForm( stellarToml.AuthServer, authRequest.ToValues(), ) if err != nil { log.WithFields(log.Fields{ "auth_server": stellarToml.AuthServer, "err": err, }).Error("Error sending request to auth server") server.Write(w, protocols.InternalServerError) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Error("Error reading auth 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 auth server") server.Write(w, protocols.InternalServerError) return } var authResponse compliance.AuthResponse err = json.Unmarshal(body, &authResponse) if err != nil { log.WithFields(log.Fields{ "status": resp.StatusCode, "body": string(body), }).Error("Error unmarshalling auth response") server.Write(w, protocols.InternalServerError) return } response := compliance.SendResponse{ AuthResponse: authResponse, TransactionXdr: txBase64, } server.Write(w, &response) }
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) }) }
// ExampleLowLevelTransaction creates and signs a simple transaction, and then // encodes it into a hex string capable of being submitted to stellar-core. // // It uses the low-level xdr facilities to create the transaction. func ExampleLowLevelTransaction() { spub, spriv, err := GenerateKeyFromSeed("SA26PHIKZM6CXDGR472SSGUQQRYXM6S437ZNHZGRM6QA4FOPLLLFRGDX") if err != nil { panic(err) } dpub, _, err := GenerateKeyFromSeed("SBQHO2IMYKXAYJFCWGXC7YKLJD2EGDPSK3IUDHVJ6OOTTKLSCK6Z6POM") if err != nil { panic(err) } asset, err := xdr.NewAsset(xdr.AssetTypeAssetTypeNative, nil) if err != nil { panic(err) } destination, err := AddressToAccountId(dpub.Address()) if err != nil { panic(err) } op := xdr.PaymentOp{ Destination: destination, Asset: asset, Amount: 50 * 10000000, } memo, err := xdr.NewMemo(xdr.MemoTypeMemoNone, nil) source, err := AddressToAccountId(spub.Address()) if err != nil { panic(err) } body, err := xdr.NewOperationBody(xdr.OperationTypePayment, op) if err != nil { panic(err) } tx := xdr.Transaction{ SourceAccount: source, Fee: 10, SeqNum: xdr.SequenceNumber(1), Memo: memo, Operations: []xdr.Operation{ {Body: body}, }, } var txBytes bytes.Buffer _, err = xdr.Marshal(&txBytes, tx) if err != nil { panic(err) } txHash := Hash(txBytes.Bytes()) signature := spriv.Sign(txHash[:]) ds := xdr.DecoratedSignature{ Hint: spriv.Hint(), Signature: xdr.Signature(signature[:]), } txe := xdr.TransactionEnvelope{ Tx: tx, Signatures: []xdr.DecoratedSignature{ds}, } var txeBytes bytes.Buffer _, err = xdr.Marshal(&txeBytes, txe) if err != nil { panic(err) } txeB64 := base64.StdEncoding.EncodeToString(txeBytes.Bytes()) fmt.Printf("tx base64: %s", txeB64) // Output: tx base64: AAAAAAU08yUQ8sHqhY8j9mXWwERfHC/3cKFSe/spAr0rGtO2AAAACgAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAA+fnTe7/v4whpBUx96oj92jfZPz7S00l3O2xeyeqWIA0AAAAAAAAAAB3NZQAAAAAAAAAAATXnnQoAAABAieruUIGcQH6RlQ+prYflPFU3nED2NvWhtaC+tgnKsqgiKURK4xo/W7EgH0+I6aQok52awbE+ksOxEQ5MLJ9eAw== }
// 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 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) }) }