// 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
}
Exemple #2
0
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(),
	}
}
Exemple #3
0
// 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
}
Exemple #4
0
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
}
Exemple #6
0
// 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
}
Exemple #8
0
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
}
Exemple #9
0
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,
	}
}
Exemple #10
0
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
}
Exemple #11
0
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)
	})
}
Exemple #12
0
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)
}
Exemple #14
0
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)
	})
}
Exemple #15
0
// 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)
}
Exemple #17
0
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)
	})
}