From b5be72b14de97a24dac7e5c9815485c83d026240 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Fri, 25 Sep 2020 14:05:15 -0700 Subject: [PATCH 1/5] Move new files to txnbuild/ via git mv --- .../txnbuild => txnbuild}/begin_sponsoring_future_reserves.go | 0 .../internal/txnbuild => txnbuild}/claim_claimable_balance.go | 0 .../internal/txnbuild => txnbuild}/create_claimable_balance.go | 0 .../txnbuild => txnbuild}/end_sponsoring_future_reserves.go | 0 .../horizon/internal/txnbuild => txnbuild}/revoke_sponsorship.go | 0 .../internal/txnbuild => txnbuild}/revoke_sponsorship_test.go | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {services/horizon/internal/txnbuild => txnbuild}/begin_sponsoring_future_reserves.go (100%) rename {services/horizon/internal/txnbuild => txnbuild}/claim_claimable_balance.go (100%) rename {services/horizon/internal/txnbuild => txnbuild}/create_claimable_balance.go (100%) rename {services/horizon/internal/txnbuild => txnbuild}/end_sponsoring_future_reserves.go (100%) rename {services/horizon/internal/txnbuild => txnbuild}/revoke_sponsorship.go (100%) rename {services/horizon/internal/txnbuild => txnbuild}/revoke_sponsorship_test.go (100%) diff --git a/services/horizon/internal/txnbuild/begin_sponsoring_future_reserves.go b/txnbuild/begin_sponsoring_future_reserves.go similarity index 100% rename from services/horizon/internal/txnbuild/begin_sponsoring_future_reserves.go rename to txnbuild/begin_sponsoring_future_reserves.go diff --git a/services/horizon/internal/txnbuild/claim_claimable_balance.go b/txnbuild/claim_claimable_balance.go similarity index 100% rename from services/horizon/internal/txnbuild/claim_claimable_balance.go rename to txnbuild/claim_claimable_balance.go diff --git a/services/horizon/internal/txnbuild/create_claimable_balance.go b/txnbuild/create_claimable_balance.go similarity index 100% rename from services/horizon/internal/txnbuild/create_claimable_balance.go rename to txnbuild/create_claimable_balance.go diff --git a/services/horizon/internal/txnbuild/end_sponsoring_future_reserves.go b/txnbuild/end_sponsoring_future_reserves.go similarity index 100% rename from services/horizon/internal/txnbuild/end_sponsoring_future_reserves.go rename to txnbuild/end_sponsoring_future_reserves.go diff --git a/services/horizon/internal/txnbuild/revoke_sponsorship.go b/txnbuild/revoke_sponsorship.go similarity index 100% rename from services/horizon/internal/txnbuild/revoke_sponsorship.go rename to txnbuild/revoke_sponsorship.go diff --git a/services/horizon/internal/txnbuild/revoke_sponsorship_test.go b/txnbuild/revoke_sponsorship_test.go similarity index 100% rename from services/horizon/internal/txnbuild/revoke_sponsorship_test.go rename to txnbuild/revoke_sponsorship_test.go From e69851f3d7f812c97bb18b82581d1b2e6f0661a9 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Fri, 25 Sep 2020 13:45:51 -0700 Subject: [PATCH 2/5] Include post-fork modifications to relevant files via partial checkout: - transaction.go, from 817967984a49b688cff8497b0dda701c627dc6dd by @abuiles - helpers.go, from e6a3c46adf64953fc0ecd48f4e9ea21a9ea29822 by @2opremio --- txnbuild/helpers.go | 14 +++++++++++++ txnbuild/transaction.go | 45 ----------------------------------------- 2 files changed, 14 insertions(+), 45 deletions(-) diff --git a/txnbuild/helpers.go b/txnbuild/helpers.go index 1b59731f03..41864b9b71 100644 --- a/txnbuild/helpers.go +++ b/txnbuild/helpers.go @@ -5,6 +5,7 @@ import ( "github.com/stellar/go/amount" "github.com/stellar/go/strkey" "github.com/stellar/go/support/errors" + "github.com/stellar/go/xdr" ) // validateStellarPublicKey returns an error if a public key is invalid. Otherwise, it returns nil. @@ -20,6 +21,19 @@ func validateStellarPublicKey(publicKey string) error { return nil } +// validateStellarSignerKey returns an error if a signerkey is invalid. Otherwise, it returns nil. +func validateStellarSignerKey(signerKey string) error { + if signerKey == "" { + return errors.New("signer key is undefined") + } + + var xdrKey xdr.SignerKey + if err := xdrKey.SetAddress(signerKey); err != nil { + return errors.Errorf("%s is not a valid stellar signer key", signerKey) + } + return nil +} + // validateStellarAsset checks if the asset supplied is a valid stellar Asset. It returns an error if the asset is // nil, has an invalid asset code or issuer. func validateStellarAsset(asset Asset) error { diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index c418cfa492..c38e24e4a5 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -384,12 +384,6 @@ func (t *FeeBumpTransaction) Hash(networkStr string) ([32]byte, error) { return network.HashTransactionInEnvelope(t.envelope, networkStr) } -// HashHex returns the network specific hash of this transaction -// encoded as a hexadecimal string. -func (t *FeeBumpTransaction) HashHex(network string) (string, error) { - return hashHex(t.envelope, network) -} - // Sign returns a new FeeBumpTransaction instance which extends the current instance // with additional signatures derived from the given list of keypair instances. func (t *FeeBumpTransaction) Sign(network string, kps ...*keypair.Full) (*FeeBumpTransaction, error) { @@ -404,45 +398,6 @@ func (t *FeeBumpTransaction) Sign(network string, kps ...*keypair.Full) (*FeeBum return newTx, nil } -// SignWithKeyString returns a new FeeBumpTransaction instance which extends the current instance -// with additional signatures derived from the given list of private key strings. -func (t *FeeBumpTransaction) SignWithKeyString(network string, keys ...string) (*FeeBumpTransaction, error) { - kps, err := stringsToKP(keys...) - if err != nil { - return nil, err - } - return t.Sign(network, kps...) -} - -// SignHashX returns a new FeeBumpTransaction instance which extends the current instance -// with HashX signature type. -// See description here: https://www.stellar.org/developers/guides/concepts/multi-sig.html#hashx. -func (t *FeeBumpTransaction) SignHashX(preimage []byte) (*FeeBumpTransaction, error) { - extendedSignatures, err := concatHashX(t.signatures, preimage) - if err != nil { - return nil, err - } - - newTx := new(FeeBumpTransaction) - *newTx = *t - newTx.signatures = extendedSignatures - return newTx, nil -} - -// AddSignatureBase64 returns a new FeeBumpTransaction instance which extends the current instance -// with an additional signature derived from the given base64-encoded signature. -func (t *FeeBumpTransaction) AddSignatureBase64(network, publicKey, signature string) (*FeeBumpTransaction, error) { - extendedSignatures, err := concatSignatureBase64(t.envelope, t.signatures, network, publicKey, signature) - if err != nil { - return nil, err - } - - newTx := new(FeeBumpTransaction) - *newTx = *t - newTx.signatures = extendedSignatures - return newTx, nil -} - // TxEnvelope returns the a xdr.TransactionEnvelope instance which is // equivalent to this transaction. func (t *FeeBumpTransaction) TxEnvelope() (xdr.TransactionEnvelope, error) { From 8f40f9137163e4ce7f96c8e6d6a3fe0680e0a22f Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Fri, 25 Sep 2020 14:14:23 -0700 Subject: [PATCH 3/5] Replace old imports with the updated top-level txnbuild/ package --- .../integration/protocol14_claimable_balance_ops_test.go | 2 +- .../internal/integration/protocol14_sponsorship_ops_test.go | 2 +- .../internal/integration/protocol14_state_verifier_test.go | 2 +- services/horizon/internal/integration/protocol14_test.go | 2 +- services/horizon/internal/test/integration.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/horizon/internal/integration/protocol14_claimable_balance_ops_test.go b/services/horizon/internal/integration/protocol14_claimable_balance_ops_test.go index 45ecbfc7c0..55cbe10e61 100644 --- a/services/horizon/internal/integration/protocol14_claimable_balance_ops_test.go +++ b/services/horizon/internal/integration/protocol14_claimable_balance_ops_test.go @@ -8,7 +8,7 @@ import ( hEffects "github.com/stellar/go/protocols/horizon/effects" "github.com/stellar/go/protocols/horizon/operations" "github.com/stellar/go/services/horizon/internal/test" - "github.com/stellar/go/services/horizon/internal/txnbuild" + "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" "github.com/stretchr/testify/assert" ) diff --git a/services/horizon/internal/integration/protocol14_sponsorship_ops_test.go b/services/horizon/internal/integration/protocol14_sponsorship_ops_test.go index 1461a400cc..fd31371a69 100644 --- a/services/horizon/internal/integration/protocol14_sponsorship_ops_test.go +++ b/services/horizon/internal/integration/protocol14_sponsorship_ops_test.go @@ -12,7 +12,7 @@ import ( "github.com/stellar/go/protocols/horizon/effects" "github.com/stellar/go/protocols/horizon/operations" "github.com/stellar/go/services/horizon/internal/test" - "github.com/stellar/go/services/horizon/internal/txnbuild" + "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" ) diff --git a/services/horizon/internal/integration/protocol14_state_verifier_test.go b/services/horizon/internal/integration/protocol14_state_verifier_test.go index 289f8d0ecd..eef3d0d9b4 100644 --- a/services/horizon/internal/integration/protocol14_state_verifier_test.go +++ b/services/horizon/internal/integration/protocol14_state_verifier_test.go @@ -10,7 +10,7 @@ import ( "github.com/stellar/go/keypair" "github.com/stellar/go/services/horizon/internal/test" - "github.com/stellar/go/services/horizon/internal/txnbuild" + "github.com/stellar/go/txnbuild" "github.com/stretchr/testify/assert" ) diff --git a/services/horizon/internal/integration/protocol14_test.go b/services/horizon/internal/integration/protocol14_test.go index ae5238b048..a3efe7e326 100644 --- a/services/horizon/internal/integration/protocol14_test.go +++ b/services/horizon/internal/integration/protocol14_test.go @@ -10,7 +10,7 @@ import ( "github.com/stellar/go/protocols/horizon/operations" "github.com/stellar/go/services/horizon/internal/codes" "github.com/stellar/go/services/horizon/internal/test" - "github.com/stellar/go/services/horizon/internal/txnbuild" + "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" "github.com/stretchr/testify/assert" ) diff --git a/services/horizon/internal/test/integration.go b/services/horizon/internal/test/integration.go index db732120a1..6f057f6b4e 100644 --- a/services/horizon/internal/test/integration.go +++ b/services/horizon/internal/test/integration.go @@ -20,8 +20,8 @@ import ( sdk "github.com/stellar/go/clients/horizonclient" "github.com/stellar/go/keypair" proto "github.com/stellar/go/protocols/horizon" - "github.com/stellar/go/services/horizon/internal/txnbuild" "github.com/stellar/go/support/errors" + "github.com/stellar/go/txnbuild" ) const IntegrationNetworkPassphrase = "Standalone Network ; February 2017" From 90f8bf3bf72ea89621f767ff103e82d60769ea62 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Fri, 25 Sep 2020 14:19:45 -0700 Subject: [PATCH 4/5] Delete the internal/txnbuild package entirely --- .../horizon/internal/txnbuild/CHANGELOG.md | 112 - services/horizon/internal/txnbuild/README.md | 98 - .../internal/txnbuild/account_merge.go | 63 - .../internal/txnbuild/account_merge_test.go | 29 - .../horizon/internal/txnbuild/allow_trust.go | 104 - .../internal/txnbuild/allow_trust_test.go | 59 - services/horizon/internal/txnbuild/asset.go | 118 - .../horizon/internal/txnbuild/asset_test.go | 104 - .../internal/txnbuild/bump_sequence.go | 54 - .../internal/txnbuild/bump_sequence_test.go | 29 - .../horizon/internal/txnbuild/change_trust.go | 104 - .../internal/txnbuild/change_trust_test.go | 81 - .../internal/txnbuild/create_account.go | 75 - .../internal/txnbuild/create_account_test.go | 56 - .../internal/txnbuild/create_passive_offer.go | 95 - .../txnbuild/create_passive_offer_test.go | 140 - .../horizon/internal/txnbuild/example_test.go | 769 ----- .../internal/txnbuild/fee_bump_test.go | 210 -- services/horizon/internal/txnbuild/helpers.go | 194 -- .../horizon/internal/txnbuild/helpers_test.go | 358 -- .../horizon/internal/txnbuild/inflation.go | 46 - .../internal/txnbuild/manage_buy_offer.go | 98 - .../txnbuild/manage_buy_offer_test.go | 172 - .../horizon/internal/txnbuild/manage_data.go | 72 - .../internal/txnbuild/manage_data_test.go | 128 - .../horizon/internal/txnbuild/manage_offer.go | 162 - .../internal/txnbuild/manage_offer_test.go | 168 - services/horizon/internal/txnbuild/memo.go | 85 - .../horizon/internal/txnbuild/memo_test.go | 51 - .../horizon/internal/txnbuild/operation.go | 70 - .../internal/txnbuild/operation_test.go | 418 --- .../horizon/internal/txnbuild/path_payment.go | 167 - .../txnbuild/path_payment_strict_send.go | 161 - .../txnbuild/path_payment_strict_send_test.go | 158 - .../internal/txnbuild/path_payment_test.go | 158 - services/horizon/internal/txnbuild/payment.go | 101 - .../horizon/internal/txnbuild/payment_test.go | 82 - services/horizon/internal/txnbuild/price.go | 63 - .../horizon/internal/txnbuild/price_test.go | 82 - .../horizon/internal/txnbuild/set_options.go | 325 -- .../internal/txnbuild/set_options_test.go | 138 - .../internal/txnbuild/signer_summary.go | 4 - .../horizon/internal/txnbuild/signers_test.go | 558 ---- .../internal/txnbuild/simple_account.go | 32 - .../horizon/internal/txnbuild/timebounds.go | 71 - .../internal/txnbuild/timebounds_test.go | 51 - .../horizon/internal/txnbuild/transaction.go | 1062 ------ .../internal/txnbuild/transaction_fee_test.go | 143 - .../internal/txnbuild/transaction_test.go | 2947 ----------------- 49 files changed, 10625 deletions(-) delete mode 100644 services/horizon/internal/txnbuild/CHANGELOG.md delete mode 100644 services/horizon/internal/txnbuild/README.md delete mode 100644 services/horizon/internal/txnbuild/account_merge.go delete mode 100644 services/horizon/internal/txnbuild/account_merge_test.go delete mode 100644 services/horizon/internal/txnbuild/allow_trust.go delete mode 100644 services/horizon/internal/txnbuild/allow_trust_test.go delete mode 100644 services/horizon/internal/txnbuild/asset.go delete mode 100644 services/horizon/internal/txnbuild/asset_test.go delete mode 100644 services/horizon/internal/txnbuild/bump_sequence.go delete mode 100644 services/horizon/internal/txnbuild/bump_sequence_test.go delete mode 100644 services/horizon/internal/txnbuild/change_trust.go delete mode 100644 services/horizon/internal/txnbuild/change_trust_test.go delete mode 100644 services/horizon/internal/txnbuild/create_account.go delete mode 100644 services/horizon/internal/txnbuild/create_account_test.go delete mode 100644 services/horizon/internal/txnbuild/create_passive_offer.go delete mode 100644 services/horizon/internal/txnbuild/create_passive_offer_test.go delete mode 100644 services/horizon/internal/txnbuild/example_test.go delete mode 100644 services/horizon/internal/txnbuild/fee_bump_test.go delete mode 100644 services/horizon/internal/txnbuild/helpers.go delete mode 100644 services/horizon/internal/txnbuild/helpers_test.go delete mode 100644 services/horizon/internal/txnbuild/inflation.go delete mode 100644 services/horizon/internal/txnbuild/manage_buy_offer.go delete mode 100644 services/horizon/internal/txnbuild/manage_buy_offer_test.go delete mode 100644 services/horizon/internal/txnbuild/manage_data.go delete mode 100644 services/horizon/internal/txnbuild/manage_data_test.go delete mode 100644 services/horizon/internal/txnbuild/manage_offer.go delete mode 100644 services/horizon/internal/txnbuild/manage_offer_test.go delete mode 100644 services/horizon/internal/txnbuild/memo.go delete mode 100644 services/horizon/internal/txnbuild/memo_test.go delete mode 100644 services/horizon/internal/txnbuild/operation.go delete mode 100644 services/horizon/internal/txnbuild/operation_test.go delete mode 100644 services/horizon/internal/txnbuild/path_payment.go delete mode 100644 services/horizon/internal/txnbuild/path_payment_strict_send.go delete mode 100644 services/horizon/internal/txnbuild/path_payment_strict_send_test.go delete mode 100644 services/horizon/internal/txnbuild/path_payment_test.go delete mode 100644 services/horizon/internal/txnbuild/payment.go delete mode 100644 services/horizon/internal/txnbuild/payment_test.go delete mode 100644 services/horizon/internal/txnbuild/price.go delete mode 100644 services/horizon/internal/txnbuild/price_test.go delete mode 100644 services/horizon/internal/txnbuild/set_options.go delete mode 100644 services/horizon/internal/txnbuild/set_options_test.go delete mode 100644 services/horizon/internal/txnbuild/signer_summary.go delete mode 100644 services/horizon/internal/txnbuild/signers_test.go delete mode 100644 services/horizon/internal/txnbuild/simple_account.go delete mode 100644 services/horizon/internal/txnbuild/timebounds.go delete mode 100644 services/horizon/internal/txnbuild/timebounds_test.go delete mode 100644 services/horizon/internal/txnbuild/transaction.go delete mode 100644 services/horizon/internal/txnbuild/transaction_fee_test.go delete mode 100644 services/horizon/internal/txnbuild/transaction_test.go diff --git a/services/horizon/internal/txnbuild/CHANGELOG.md b/services/horizon/internal/txnbuild/CHANGELOG.md deleted file mode 100644 index 5ad9969bca..0000000000 --- a/services/horizon/internal/txnbuild/CHANGELOG.md +++ /dev/null @@ -1,112 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this -file. This project adheres to [Semantic Versioning](http://semver.org/). - -## [v3.1.0](https://github.com/stellar/go/releases/tag/horizonclient-v3.1.0) - 2020-05-14 - -* Fix bug which occurs when parsing xdr offers with prices that require more than 7 decimals of precision ([#2588](https://github.com/stellar/go/pull/2588)) -* Add `AddSignatureBase64` function to both `Transaction` and `FeeBumpTransaction` objects for adding a base64-encoded signature. [#2586](https://github.com/stellar/go/pull/2586) - -## [v3.0.1](https://github.com/stellar/go/releases/tag/horizonclient-v3.0.1) - 2020-05-11 - -* Fix bug which occurs when parsing transactions with manage data operations containing nil values ([#2573](https://github.com/stellar/go/pull/2573)) - -## [v3.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v3.0.0) - 2020-04-28 - -### Breaking changes - -* The `Account` interface has been extended to include `GetSequenceNumber() (int64, error)`. Also, `IncrementSequenceNumber()` now returns an `(int64, error)` pair instead of a `(xdr.SequenceNumber, error)` pair. -* Refactor workflow for creating and signing transactions. Previously, you could create a transaction envelope by populating a `Transaction` instance and calling the `Build()` function on the `Transaction` instance. - -`Transaction` is now an opaque type which has accessor functions like `SourceAccount() SimpleAccount`, `Memo() Memo`, etc. The motivation behind this change is to make `Transaction` more immutable. Here is an example of how to use the new transaction type: -```go - kp := keypair.MustParse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := txnbuild.Payment{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - Asset: NativeAsset{}, - } - - tx, err := txnbuild.NewTransaction( - txnbuild.TransactionParams{ - SourceAccount: &sourceAccount, - // If IncrementSequenceNum is true, NewTransaction() will call `sourceAccount.IncrementSequenceNumber()` - // to obtain the sequence number for the transaction. - // If IncrementSequenceNum is false, NewTransaction() will call `sourceAccount.GetSequenceNumber()` - // to obtain the sequence number for the transaction. - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) -``` - -* `TransactionFromXDR` now has the following signature `TransactionFromXDR(txeB64 string) (*GenericTransaction, error)`. A `GenericTransaction` is a container which can be unpacked into either a `Transaction` or a `FeeBumpTransaction`. -* `BuildChallengeTx` now returns a `Transaction` instance instead of the base 64 string encoding of the SEP 10 challenge transaction. -* `VerifyChallengeTx` has been removed. Use `VerifyChallengeTxThreshold` or `VerifyChallengeTxSigners` instead. - -### Add - -* Add `NewFeeBumpTransaction(params FeeBumpTransactionParams) (*FeeBumpTransaction, error)` function for creating [fee bump transactions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md). Note that fee bump transactions will only be accepted by Stellar Core once Protocol 13 is enabled. - -### Updates - -* `AllowTrust` supports [CAP0018](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0018.md) Fine-Grained Control of Authorization by exposing a `AuthorizeToMaintainLiabilities` boolean field. -* `ReadChallengeTx` will reject any challenge transactions which are fee bump transactions. -* `ReadChallengeTx` will reject any challenge transactions which contain a [MuxedAccount](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0027.md) with a memo ID. - -### Remove - -* Dropped support for Go 1.12. - -## [v1.5.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.5.0) - 2019-10-09 - -* Dropped support for Go 1.10, 1.11. -* Add support for stellar-core [protocol 12](https://github.com/stellar/stellar-core/releases/tag/v12.0.0), which implements [CAP-0024](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0024.md) ("Make PathPayment Symmetrical"). ([#1737](https://github.com/stellar/go/issues/1737)). -* **Deprecated:** Following CAP-0024, the operation `txnbuild.PathPayment` is now deprecated in favour of [`txnbuild.PathPaymentStrictReceive`](https://godoc.org/github.com/stellar/go/txnbuild#PathPaymentStrictReceive), and will be removed in a future release. This is a rename - the new operation behaves identically to the old one. Client code should be updated to use the new operation. -* **Add:** New operation [`txnbuild.PathPaymentStrictSend`](https://godoc.org/github.com/stellar/go/txnbuild#PathPaymentStrictSend) allows a path payment to be made where the amount sent is specified, and the amount received can vary. - -## [v1.4.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.4.0) - 2019-08-09 - -* Add `BuildChallengeTx` function for building [SEP-10](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md) challenge transaction([#1466](https://github.com/stellar/go/issues/1466)). -* Add `VerifyChallengeTx` method for verifying [SEP-10](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md) challenge transaction([#1530](https://github.com/stellar/go/issues/1530)). -* Add `TransactionFromXDR` function for building `txnbuild.Transaction` struct from a base64 XDR transaction envelope[#1329](https://github.com/stellar/go/issues/1329). -* Fix bug that allowed multiple calls to `Transaction.Build` increment the number of operations in a transaction [#1448](https://github.com/stellar/go/issues/1448). -* Add `Transaction.SignWithKeyString` helper method for signing transactions using secret keys as strings.([#1564](https://github.com/stellar/go/issues/1564)) - - -## [v1.3.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.3.0) - 2019-07-08 - -* Add support for getting the hex-encoded transaction hash with `Transaction.HashHex` method. -* `TransactionEnvelope` is now available after building a transaction(`Transaction.Build`). Previously, this was only available after signing a transaction. ([#1376](https://github.com/stellar/go/pull/1376)) -* Add support for getting the `TransactionEnvelope` struct with `Transaction.TxEnvelope` method ([#1415](https://github.com/stellar/go/issues/1415)). -* `AllowTrust` operations no longer requires the asset issuer, only asset code is required ([#1330](https://github.com/stellar/go/issues/1330)). -* `Transaction.SetDefaultFee` method is deprecated and will be removed in the next major release ([#1221](https://github.com/stellar/go/issues/1221)). -* `Transaction.TransactionFee` method has been added to get the fee that will be paid for a transaction. -* `Transaction.SignHashX` method adds support for signing transactions with hash(x) signature types. - -## [v1.2.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.2.0) - 2019-05-16 - -* In addition to account responses from horizon, transactions and operations can now be built with txnbuild.SimpleAccount structs constructed locally ([#1266](https://github.com/stellar/go/issues/1266)). -* Added `MaxTrustlineLimit` which represents the maximum value for a trustline limit ([#1265](https://github.com/stellar/go/issues/1265)). -* ChangeTrust operation with no `Limit` field set now defaults to `MaxTrustlineLimit` ([#1265](https://github.com/stellar/go/issues/1265)). -* Add support for building `ManageBuyOffer` operation ([#1165](https://github.com/stellar/go/issues/1165)). -* Fix bug in ChangeTrust operation builder ([1296](https://github.com/stellar/go/issues/1296)). - -## [v1.1.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.1.0) - 2019-02-02 - -* Support for multiple signatures ([#1198](https://github.com/stellar/go/pull/1198)) - -## [v1.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.0) - 2019-04-26 - -* Initial release diff --git a/services/horizon/internal/txnbuild/README.md b/services/horizon/internal/txnbuild/README.md deleted file mode 100644 index fc3d208500..0000000000 --- a/services/horizon/internal/txnbuild/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# txnbuild - -`txnbuild` is a [Stellar SDK](https://www.stellar.org/developers/reference/), implemented in [Go](https://golang.org/). It provides a reference implementation of the complete [set of operations](https://www.stellar.org/developers/guides/concepts/list-of-operations.html) that compose [transactions](https://www.stellar.org/developers/guides/concepts/transactions.html) for the Stellar distributed ledger. - -This project is maintained by the Stellar Development Foundation. - -```golang - import ( - "log" - - "github.com/stellar/go/clients/horizonclient" - "github.com/stellar/go/keypair" - "github.com/stellar/go/network" - "github.com/stellar/go/txnbuild" - ) - - // Make a keypair for a known account from a secret seed - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - - // Get the current state of the account from the network - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - if err != nil { - log.Fatalln(err) - } - - // Build an operation to create and fund a new account - op := txnbuild.CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - } - - // Construct the transaction that holds the operations to execute on the network - tx, err := txnbuild.NewTransaction( - txnbuild.TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []txnbuild.Operation{&op}, - BaseFee: txnbuild.MinBaseFee, - Timebounds: txnbuild.NewTimeout(300), - }, - ) - if err != nil { - log.Fatalln(err) - ) - - // Sign the transaction - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - if err != nil { - log.Fatalln(err) - ) - - // Get the base 64 encoded transaction envelope - txe, err := tx.Base64() - if err != nil { - log.Fatalln(err) - } - - // Send the transaction to the network - resp, err := client.SubmitTransactionXDR(txe) - if err != nil { - log.Fatalln(err) - } -``` - -## Getting Started -This library is aimed at developers building Go applications on top of the [Stellar network](https://www.stellar.org/). Transactions constructed by this library may be submitted to any Horizon instance for processing onto the ledger, using any Stellar SDK client. The recommended client for Go programmers is [horizonclient](https://github.com/stellar/go/tree/master/clients/horizonclient). Together, these two libraries provide a complete Stellar SDK. - -* The [txnbuild API reference](https://godoc.org/github.com/stellar/go/txnbuild). -* The [horizonclient API reference](https://godoc.org/github.com/stellar/go/clients/horizonclient). - -An easy-to-follow demonstration that exercises this SDK on the TestNet with actual accounts is also included! See the [Demo](#demo) section below. - -### Prerequisites -* Go 1.13 or greater -* [Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies - -### Installing -* `go get github.com/stellar/go/clients/txnbuild` - -## Running the tests -Run the unit tests from the package directory: `go test` - -## Demo -To see the SDK in action, build and run the demo: -* Enter the demo directory: `cd $GOPATH/src/github.com/stellar/go/txnbuild/cmd/demo` -* Build the demo: `go build` -* Run the demo: `./demo init` - - -## Contributing -Please read [Code of Conduct](https://github.com/stellar/.github/blob/master/CODE_OF_CONDUCT.md) to understand this project's communication rules. - -To submit improvements and fixes to this library, please see [CONTRIBUTING](../CONTRIBUTING.md). - -## License -This project is licensed under the Apache License - see the [LICENSE](../../LICENSE-APACHE.txt) file for details. diff --git a/services/horizon/internal/txnbuild/account_merge.go b/services/horizon/internal/txnbuild/account_merge.go deleted file mode 100644 index 875e629490..0000000000 --- a/services/horizon/internal/txnbuild/account_merge.go +++ /dev/null @@ -1,63 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// AccountMerge represents the Stellar merge account operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type AccountMerge struct { - Destination string - SourceAccount Account -} - -// BuildXDR for AccountMerge returns a fully configured XDR Operation. -func (am *AccountMerge) BuildXDR() (xdr.Operation, error) { - var xdrOp xdr.MuxedAccount - - err := xdrOp.SetAddress(am.Destination) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") - } - - opType := xdr.OperationTypeAccountMerge - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, am.SourceAccount) - return op, nil -} - -// FromXDR for AccountMerge initialises the txnbuild struct from the corresponding xdr Operation. -func (am *AccountMerge) FromXDR(xdrOp xdr.Operation) error { - if xdrOp.Body.Type != xdr.OperationTypeAccountMerge { - return errors.New("error parsing account_merge operation from xdr") - } - - am.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - if xdrOp.Body.Destination != nil { - aid := xdrOp.Body.Destination.ToAccountId() - am.Destination = aid.Address() - } - - return nil -} - -// Validate for AccountMerge validates the required struct fields. It returns an error if any of the fields are -// invalid. Otherwise, it returns nil. -func (am *AccountMerge) Validate() error { - _, err := xdr.AddressToAccountId(am.Destination) - if err != nil { - return NewValidationError("Destination", err.Error()) - } - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (am *AccountMerge) GetSourceAccount() Account { - return am.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/account_merge_test.go b/services/horizon/internal/txnbuild/account_merge_test.go deleted file mode 100644 index ebf002526d..0000000000 --- a/services/horizon/internal/txnbuild/account_merge_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAccountMergeValidate(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484298)) - - accountMerge := AccountMerge{ - Destination: "GBAV", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&accountMerge}, - Timebounds: NewInfiniteTimeout(), - BaseFee: MinBaseFee, - }, - ) - if assert.Error(t, err) { - expected := "strkey is 4 bytes long; minimum valid length is 5" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/allow_trust.go b/services/horizon/internal/txnbuild/allow_trust.go deleted file mode 100644 index 9fd17be681..0000000000 --- a/services/horizon/internal/txnbuild/allow_trust.go +++ /dev/null @@ -1,104 +0,0 @@ -package txnbuild - -import ( - "bytes" - - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// AllowTrust represents the Stellar allow trust operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type AllowTrust struct { - Trustor string - Type Asset - Authorize bool - AuthorizeToMaintainLiabilities bool - SourceAccount Account -} - -// BuildXDR for AllowTrust returns a fully configured XDR Operation. -func (at *AllowTrust) BuildXDR() (xdr.Operation, error) { - var xdrOp xdr.AllowTrustOp - - // Set XDR address associated with the trustline - err := xdrOp.Trustor.SetAddress(at.Trustor) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set trustor address") - } - - // Validate this is an issued asset - if at.Type.IsNative() { - return xdr.Operation{}, errors.New("trustline doesn't exist for a native (XLM) asset") - } - - // AllowTrust has a special asset type - map to it - xdrAsset := xdr.Asset{} - - xdrOp.Asset, err = xdrAsset.ToAllowTrustOpAsset(at.Type.GetCode()) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "can't convert asset for trustline to allow trust asset type") - } - - // Set XDR auth flag - if at.Authorize { - xdrOp.Authorize = xdr.Uint32(xdr.TrustLineFlagsAuthorizedFlag) - } else if at.AuthorizeToMaintainLiabilities { - xdrOp.Authorize = xdr.Uint32(xdr.TrustLineFlagsAuthorizedToMaintainLiabilitiesFlag) - } - - opType := xdr.OperationTypeAllowTrust - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, at.SourceAccount) - return op, nil -} - -// FromXDR for AllowTrust initialises the txnbuild struct from the corresponding xdr Operation. -func (at *AllowTrust) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetAllowTrustOp() - if !ok { - return errors.New("error parsing allow_trust operation from xdr") - } - - at.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - at.Trustor = result.Trustor.Address() - flag := xdr.TrustLineFlags(result.Authorize) - at.Authorize = flag.IsAuthorized() - at.AuthorizeToMaintainLiabilities = flag.IsAuthorizedToMaintainLiabilitiesFlag() - //Because AllowTrust has a special asset type, we don't use assetFromXDR() here. - if result.Asset.Type == xdr.AssetTypeAssetTypeCreditAlphanum4 { - code := bytes.Trim(result.Asset.AssetCode4[:], "\x00") - at.Type = CreditAsset{Code: string(code[:])} - } - if result.Asset.Type == xdr.AssetTypeAssetTypeCreditAlphanum12 { - code := bytes.Trim(result.Asset.AssetCode12[:], "\x00") - at.Type = CreditAsset{Code: string(code[:])} - } - - return nil -} - -// Validate for AllowTrust validates the required struct fields. It returns an error if any of the fields are -// invalid. Otherwise, it returns nil. -func (at *AllowTrust) Validate() error { - err := validateStellarPublicKey(at.Trustor) - if err != nil { - return NewValidationError("Trustor", err.Error()) - } - - err = validateAllowTrustAsset(at.Type) - if err != nil { - return NewValidationError("Type", err.Error()) - } - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (at *AllowTrust) GetSourceAccount() Account { - return at.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/allow_trust_test.go b/services/horizon/internal/txnbuild/allow_trust_test.go deleted file mode 100644 index 4800a82beb..0000000000 --- a/services/horizon/internal/txnbuild/allow_trust_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAllowTrustValidateAsset(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484366)) - - issuedAsset := CreditAsset{"", kp1.Address()} - allowTrust := AllowTrust{ - Trustor: kp1.Address(), - Type: issuedAsset, - Authorize: true, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&allowTrust}, - Timebounds: NewInfiniteTimeout(), - BaseFee: MinBaseFee, - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.AllowTrust operation: Field: Type, Error: asset code length must be between 1 and 12 characters" - assert.Contains(t, err.Error(), expected) - } -} - -func TestAllowTrustValidateTrustor(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484366)) - - issuedAsset := CreditAsset{"ABCD", kp1.Address()} - allowTrust := AllowTrust{ - Trustor: "", - Type: issuedAsset, - Authorize: true, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&allowTrust}, - Timebounds: NewInfiniteTimeout(), - BaseFee: MinBaseFee, - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.AllowTrust operation: Field: Trustor, Error: public key is undefined" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/asset.go b/services/horizon/internal/txnbuild/asset.go deleted file mode 100644 index 3a2fdb97b0..0000000000 --- a/services/horizon/internal/txnbuild/asset.go +++ /dev/null @@ -1,118 +0,0 @@ -package txnbuild - -import ( - "bytes" - - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// AssetType represents the type of a Stellar asset. -type AssetType xdr.AssetType - -// AssetTypeNative, AssetTypeCreditAlphanum4, AssetTypeCreditAlphanum12 enumerate the different -// types of asset on the Stellar network. -const ( - AssetTypeNative AssetType = AssetType(xdr.AssetTypeAssetTypeNative) - AssetTypeCreditAlphanum4 AssetType = AssetType(xdr.AssetTypeAssetTypeCreditAlphanum4) - AssetTypeCreditAlphanum12 AssetType = AssetType(xdr.AssetTypeAssetTypeCreditAlphanum12) -) - -// Asset represents a Stellar asset. -type Asset interface { - GetType() (AssetType, error) - IsNative() bool - GetCode() string - GetIssuer() string - ToXDR() (xdr.Asset, error) -} - -// NativeAsset represents the native XLM asset. -type NativeAsset struct{} - -// GetType for NativeAsset returns the enum type of the asset. -func (na NativeAsset) GetType() (AssetType, error) { - return AssetTypeNative, nil -} - -// IsNative for NativeAsset returns true (this is an XLM asset). -func (na NativeAsset) IsNative() bool { return true } - -// GetCode for NativeAsset returns an empty string (XLM doesn't have a code). -func (na NativeAsset) GetCode() string { return "" } - -// GetIssuer for NativeAsset returns an empty string (XLM doesn't have an issuer). -func (na NativeAsset) GetIssuer() string { return "" } - -// ToXDR for NativeAsset produces a corresponding XDR asset. -func (na NativeAsset) ToXDR() (xdr.Asset, error) { - xdrAsset := xdr.Asset{} - err := xdrAsset.SetNative() - if err != nil { - return xdr.Asset{}, err - } - return xdrAsset, nil -} - -// CreditAsset represents non-XLM assets on the Stellar network. -type CreditAsset struct { - Code string - Issuer string -} - -// GetType for CreditAsset returns the enum type of the asset, based on its code length. -func (ca CreditAsset) GetType() (AssetType, error) { - switch { - case len(ca.Code) >= 1 && len(ca.Code) <= 4: - return AssetTypeCreditAlphanum4, nil - case len(ca.Code) >= 5 && len(ca.Code) <= 12: - return AssetTypeCreditAlphanum12, nil - default: - return AssetTypeCreditAlphanum4, errors.New("asset code length must be between 1 and 12 characters") - } -} - -// IsNative for CreditAsset returns false (this is not an XLM asset). -func (ca CreditAsset) IsNative() bool { return false } - -// GetCode for CreditAsset returns the asset code. -func (ca CreditAsset) GetCode() string { return ca.Code } - -// GetIssuer for CreditAsset returns the address of the issuing account. -func (ca CreditAsset) GetIssuer() string { return ca.Issuer } - -// ToXDR for CreditAsset produces a corresponding XDR asset. -func (ca CreditAsset) ToXDR() (xdr.Asset, error) { - xdrAsset := xdr.Asset{} - var issuer xdr.AccountId - - err := issuer.SetAddress(ca.Issuer) - if err != nil { - return xdr.Asset{}, err - } - - err = xdrAsset.SetCredit(ca.Code, issuer) - if err != nil { - return xdr.Asset{}, errors.Wrap(err, "asset code length must be between 1 and 12 characters") - } - - return xdrAsset, nil -} - -// to do: consider exposing function or adding it to asset interface -func assetFromXDR(xAsset xdr.Asset) (Asset, error) { - switch xAsset.Type { - case xdr.AssetTypeAssetTypeNative: - return NativeAsset{}, nil - case xdr.AssetTypeAssetTypeCreditAlphanum4: - code := bytes.Trim(xAsset.AlphaNum4.AssetCode[:], "\x00") - return CreditAsset{Code: string(code[:]), - Issuer: xAsset.AlphaNum4.Issuer.Address()}, nil - case xdr.AssetTypeAssetTypeCreditAlphanum12: - code := bytes.Trim(xAsset.AlphaNum12.AssetCode[:], "\x00") - return CreditAsset{Code: string(code[:]), - Issuer: xAsset.AlphaNum12.Issuer.Address()}, nil - } - - return nil, errors.New("invalid asset") -} diff --git a/services/horizon/internal/txnbuild/asset_test.go b/services/horizon/internal/txnbuild/asset_test.go deleted file mode 100644 index fc0926d99f..0000000000 --- a/services/horizon/internal/txnbuild/asset_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/stellar/go/xdr" -) - -func TestNativeAssetToXDR(t *testing.T) { - asset := NativeAsset{} - - received, err := asset.ToXDR() - assert.Nil(t, err) - - expected := xdr.Asset{Type: xdr.AssetTypeAssetTypeNative} - assert.Equal(t, expected, received, "Empty asset converts to native XDR") -} - -func TestAlphaNum4AssetToXDR(t *testing.T) { - asset := CreditAsset{ - Code: "USD", - Issuer: newKeypair0().Address(), - } - var xdrAssetCode [4]byte - copy(xdrAssetCode[:], asset.Code) - var xdrIssuer xdr.AccountId - require.NoError(t, xdrIssuer.SetAddress(asset.Issuer)) - - received, err := asset.ToXDR() - assert.Nil(t, err) - - expected := xdr.Asset{Type: xdr.AssetTypeAssetTypeCreditAlphanum4, - AlphaNum4: &xdr.AssetAlphaNum4{ - AssetCode: xdrAssetCode, - Issuer: xdrIssuer, - }} - assert.Equal(t, expected, received, "4 digit codes ok") -} - -func TestAlphaNum12AssetToXDR(t *testing.T) { - asset := CreditAsset{ - Code: "MEGAUSD", - Issuer: newKeypair0().Address(), - } - var xdrAssetCode [12]byte - copy(xdrAssetCode[:], asset.Code) - var xdrIssuer xdr.AccountId - require.NoError(t, xdrIssuer.SetAddress(asset.Issuer)) - - received, err := asset.ToXDR() - assert.Nil(t, err) - - expected := xdr.Asset{Type: xdr.AssetTypeAssetTypeCreditAlphanum12, - AlphaNum12: &xdr.AssetAlphaNum12{ - AssetCode: xdrAssetCode, - Issuer: xdrIssuer, - }} - assert.Equal(t, expected, received, "12 digit codes ok") -} - -func TestCodeTooShort(t *testing.T) { - asset := CreditAsset{ - Code: "", - Issuer: newKeypair0().Address(), - } - var xdrAssetCode [12]byte - copy(xdrAssetCode[:], asset.Code) - var xdrIssuer xdr.AccountId - require.NoError(t, xdrIssuer.SetAddress(asset.Issuer)) - - _, err := asset.ToXDR() - expectedErrMsg := "asset code length must be between 1 and 12 characters: Asset code length is invalid" - require.EqualError(t, err, expectedErrMsg, "Minimum code length should be enforced") -} - -func TestCodeTooLong(t *testing.T) { - asset := CreditAsset{ - Code: "THIRTEENCHARS", - Issuer: newKeypair0().Address(), - } - var xdrAssetCode [12]byte - copy(xdrAssetCode[:], asset.Code) - var xdrIssuer xdr.AccountId - require.NoError(t, xdrIssuer.SetAddress(asset.Issuer)) - - _, err := asset.ToXDR() - expectedErrMsg := "asset code length must be between 1 and 12 characters: Asset code length is invalid" - require.EqualError(t, err, expectedErrMsg, "Maximum code length should be enforced") -} - -func TestBadIssuer(t *testing.T) { - asset := CreditAsset{ - Code: "USD", - Issuer: "DOESNTLOOKLIKEANADDRESS", - } - var xdrAssetCode [4]byte - copy(xdrAssetCode[:], asset.Code) - var xdrIssuer xdr.AccountId - expectedErrMsg := "non-canonical strkey; unused bits should be set to 0" - require.EqualError(t, xdrIssuer.SetAddress(asset.Issuer), expectedErrMsg, "Issuer address should be validated") -} diff --git a/services/horizon/internal/txnbuild/bump_sequence.go b/services/horizon/internal/txnbuild/bump_sequence.go deleted file mode 100644 index 5102483b77..0000000000 --- a/services/horizon/internal/txnbuild/bump_sequence.go +++ /dev/null @@ -1,54 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// BumpSequence represents the Stellar bump sequence operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type BumpSequence struct { - BumpTo int64 - SourceAccount Account -} - -// BuildXDR for BumpSequence returns a fully configured XDR Operation. -func (bs *BumpSequence) BuildXDR() (xdr.Operation, error) { - opType := xdr.OperationTypeBumpSequence - xdrOp := xdr.BumpSequenceOp{BumpTo: xdr.SequenceNumber(bs.BumpTo)} - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, bs.SourceAccount) - return op, nil -} - -// FromXDR for BumpSequence initialises the txnbuild struct from the corresponding xdr Operation. -func (bs *BumpSequence) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetBumpSequenceOp() - if !ok { - return errors.New("error parsing bump_sequence operation from xdr") - } - - bs.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - bs.BumpTo = int64(result.BumpTo) - return nil -} - -// Validate for BumpSequence validates the required struct fields. It returns an error if any of the fields are -// invalid. Otherwise, it returns nil. -func (bs *BumpSequence) Validate() error { - err := validateAmount(bs.BumpTo) - if err != nil { - return NewValidationError("BumpTo", err.Error()) - } - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (bs *BumpSequence) GetSourceAccount() Account { - return bs.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/bump_sequence_test.go b/services/horizon/internal/txnbuild/bump_sequence_test.go deleted file mode 100644 index 3f8d470fed..0000000000 --- a/services/horizon/internal/txnbuild/bump_sequence_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBumpSequenceValidate(t *testing.T) { - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - bumpSequence := BumpSequence{ - BumpTo: -10, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&bumpSequence}, - Timebounds: NewInfiniteTimeout(), - BaseFee: MinBaseFee, - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.BumpSequence operation: Field: BumpTo, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/change_trust.go b/services/horizon/internal/txnbuild/change_trust.go deleted file mode 100644 index 2a80a3da22..0000000000 --- a/services/horizon/internal/txnbuild/change_trust.go +++ /dev/null @@ -1,104 +0,0 @@ -package txnbuild - -import ( - "math" - - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// ChangeTrust represents the Stellar change trust operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html. -// If Limit is omitted, it defaults to txnbuild.MaxTrustlineLimit. -type ChangeTrust struct { - Line Asset - Limit string - SourceAccount Account -} - -// MaxTrustlineLimit represents the maximum value that can be set as a trustline limit. -var MaxTrustlineLimit = amount.StringFromInt64(math.MaxInt64) - -// RemoveTrustlineOp returns a ChangeTrust operation to remove the trustline of the described asset, -// by setting the limit to "0". -func RemoveTrustlineOp(issuedAsset Asset) ChangeTrust { - return ChangeTrust{ - Line: issuedAsset, - Limit: "0", - } -} - -// BuildXDR for ChangeTrust returns a fully configured XDR Operation. -func (ct *ChangeTrust) BuildXDR() (xdr.Operation, error) { - if ct.Line.IsNative() { - return xdr.Operation{}, errors.New("trustline cannot be extended to a native (XLM) asset") - } - xdrLine, err := ct.Line.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "can't convert trustline asset to XDR") - } - - if ct.Limit == "" { - ct.Limit = MaxTrustlineLimit - } - - xdrLimit, err := amount.Parse(ct.Limit) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse limit amount") - } - - opType := xdr.OperationTypeChangeTrust - xdrOp := xdr.ChangeTrustOp{ - Line: xdrLine, - Limit: xdrLimit, - } - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, ct.SourceAccount) - return op, nil -} - -// FromXDR for ChangeTrust initialises the txnbuild struct from the corresponding xdr Operation. -func (ct *ChangeTrust) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetChangeTrustOp() - if !ok { - return errors.New("error parsing change_trust operation from xdr") - } - - ct.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - ct.Limit = amount.String(result.Limit) - asset, err := assetFromXDR(result.Line) - if err != nil { - return errors.Wrap(err, "error parsing asset in change_trust operation") - } - ct.Line = asset - return nil -} - -// Validate for ChangeTrust validates the required struct fields. It returns an error if any of the fields are -// invalid. Otherwise, it returns nil. -func (ct *ChangeTrust) Validate() error { - // only validate limit if it has a value. Empty limit is set to the max trustline limit. - if ct.Limit != "" { - err := validateAmount(ct.Limit) - if err != nil { - return NewValidationError("Limit", err.Error()) - } - } - - err := validateChangeTrustAsset(ct.Line) - if err != nil { - return NewValidationError("Line", err.Error()) - } - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (ct *ChangeTrust) GetSourceAccount() Account { - return ct.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/change_trust_test.go b/services/horizon/internal/txnbuild/change_trust_test.go deleted file mode 100644 index 4914099475..0000000000 --- a/services/horizon/internal/txnbuild/change_trust_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stellar/go/network" - "github.com/stretchr/testify/assert" -) - -func TestChangeTrustMaxLimit(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - changeTrust := ChangeTrust{ - Line: CreditAsset{"ABCD", kp0.Address()}, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&changeTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - // https://www.stellar.org/laboratory/#xdr-viewer?input=AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAA4Nxt4XJcrGZRYrUvrOc1sooiQ%2BQdEk1suS1wo%2BoucsV%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwAAAAAAAAAB6i5yxQAAAEBen%2BAa821qqfb%2BASHhCg074ulPcCbIsUNvzUg2x92R%2FvRRKIGF5RztvPGBkktWkXLarDe5yqlBA%2BL3zpcVs%2BwB&type=TransactionEnvelope&network=test - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAA4Nxt4XJcrGZRYrUvrOc1sooiQ+QdEk1suS1wo+oucsV//////////wAAAAAAAAAB6i5yxQAAAEBen+Aa821qqfb+ASHhCg074ulPcCbIsUNvzUg2x92R/vRRKIGF5RztvPGBkktWkXLarDe5yqlBA+L3zpcVs+wB" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestChangeTrustValidateInvalidAsset(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - changeTrust := ChangeTrust{ - Line: NativeAsset{}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&changeTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ChangeTrust operation: Field: Line, Error: native (XLM) asset type is not allowed" - assert.Contains(t, err.Error(), expected) - } -} - -func TestChangeTrustValidateInvalidLimit(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - changeTrust := ChangeTrust{ - Line: CreditAsset{"ABCD", kp0.Address()}, - Limit: "-1", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&changeTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ChangeTrust operation: Field: Limit, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/create_account.go b/services/horizon/internal/txnbuild/create_account.go deleted file mode 100644 index 1d044639ee..0000000000 --- a/services/horizon/internal/txnbuild/create_account.go +++ /dev/null @@ -1,75 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// CreateAccount represents the Stellar create account operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type CreateAccount struct { - Destination string - Amount string - SourceAccount Account -} - -// BuildXDR for CreateAccount returns a fully configured XDR Operation. -func (ca *CreateAccount) BuildXDR() (xdr.Operation, error) { - var xdrOp xdr.CreateAccountOp - - err := xdrOp.Destination.SetAddress(ca.Destination) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") - } - - xdrOp.StartingBalance, err = amount.Parse(ca.Amount) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse amount") - } - - opType := xdr.OperationTypeCreateAccount - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, ca.SourceAccount) - return op, nil -} - -// FromXDR for CreateAccount initialises the txnbuild struct from the corresponding xdr Operation. -func (ca *CreateAccount) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetCreateAccountOp() - if !ok { - return errors.New("error parsing create_account operation from xdr") - } - - ca.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - ca.Destination = result.Destination.Address() - ca.Amount = amount.String(result.StartingBalance) - - return nil -} - -// Validate for CreateAccount validates the required struct fields. It returns an error if any of the fields are -// invalid. Otherwise, it returns nil. -func (ca *CreateAccount) Validate() error { - err := validateStellarPublicKey(ca.Destination) - if err != nil { - return NewValidationError("Destination", err.Error()) - } - - err = validateAmount(ca.Amount) - if err != nil { - return NewValidationError("Amount", err.Error()) - } - - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (ca *CreateAccount) GetSourceAccount() Account { - return ca.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/create_account_test.go b/services/horizon/internal/txnbuild/create_account_test.go deleted file mode 100644 index 0e941d0068..0000000000 --- a/services/horizon/internal/txnbuild/create_account_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCreateAccountValidateDestination(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - - createAccount := CreateAccount{ - Destination: "", - Amount: "43", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.CreateAccount operation: Field: Destination, Error: public key is undefined" - assert.Contains(t, err.Error(), expected) - } -} - -func TestCreateAccountValidateAmount(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - - createAccount := CreateAccount{ - Destination: "GDYNXQFHU6W5RBW2CCCDDAAU3TMTSU2RMGIBM6HGHAR4NJJKY3IJETHT", - Amount: "", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.CreateAccount operation: Field: Amount, Error: invalid amount format" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/create_passive_offer.go b/services/horizon/internal/txnbuild/create_passive_offer.go deleted file mode 100644 index 32dda785c4..0000000000 --- a/services/horizon/internal/txnbuild/create_passive_offer.go +++ /dev/null @@ -1,95 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// CreatePassiveSellOffer represents the Stellar create passive offer operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type CreatePassiveSellOffer struct { - Selling Asset - Buying Asset - Amount string - Price string - price price - SourceAccount Account -} - -// BuildXDR for CreatePassiveSellOffer returns a fully configured XDR Operation. -func (cpo *CreatePassiveSellOffer) BuildXDR() (xdr.Operation, error) { - xdrSelling, err := cpo.Selling.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") - } - - xdrBuying, err := cpo.Buying.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Buying' field") - } - - xdrAmount, err := amount.Parse(cpo.Amount) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse 'Amount'") - } - - if err = cpo.price.parse(cpo.Price); err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse 'Price'") - } - - xdrOp := xdr.CreatePassiveSellOfferOp{ - Selling: xdrSelling, - Buying: xdrBuying, - Amount: xdrAmount, - Price: cpo.price.toXDR(), - } - - opType := xdr.OperationTypeCreatePassiveSellOffer - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, cpo.SourceAccount) - return op, nil -} - -// FromXDR for CreatePassiveSellOffer initialises the txnbuild struct from the corresponding xdr Operation. -func (cpo *CreatePassiveSellOffer) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetCreatePassiveSellOfferOp() - if !ok { - return errors.New("error parsing create_passive_sell_offer operation from xdr") - } - - cpo.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - cpo.Amount = amount.String(result.Amount) - if result.Price != (xdr.Price{}) { - cpo.price.fromXDR(result.Price) - cpo.Price = cpo.price.string() - } - buyingAsset, err := assetFromXDR(result.Buying) - if err != nil { - return errors.Wrap(err, "error parsing buying_asset in create_passive_sell_offer operation") - } - cpo.Buying = buyingAsset - - sellingAsset, err := assetFromXDR(result.Selling) - if err != nil { - return errors.Wrap(err, "error parsing selling_asset in create_passive_sell_offer operation") - } - cpo.Selling = sellingAsset - return nil -} - -// Validate for CreatePassiveSellOffer validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (cpo *CreatePassiveSellOffer) Validate() error { - return validatePassiveOffer(cpo.Buying, cpo.Selling, cpo.Amount, cpo.Price) -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (cpo *CreatePassiveSellOffer) GetSourceAccount() Account { - return cpo.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/create_passive_offer_test.go b/services/horizon/internal/txnbuild/create_passive_offer_test.go deleted file mode 100644 index f2fa34f070..0000000000 --- a/services/horizon/internal/txnbuild/create_passive_offer_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/xdr" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCreatePassiveSellOfferValidateBuyingAsset(t *testing.T) { - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761100)) - - createPassiveOffer := CreatePassiveSellOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", ""}, - Amount: "10", - Price: "1.0", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createPassiveOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.CreatePassiveSellOffer operation: Field: Buying, Error: asset issuer: public key is undefined" - assert.Contains(t, err.Error(), expected) - } -} - -func TestCreatePassiveSellOfferValidateSellingAsset(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761100)) - - createPassiveOffer := CreatePassiveSellOffer{ - Selling: CreditAsset{"ABCD0123456789", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "10", - Price: "1.0", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createPassiveOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := `validation failed for *txnbuild.CreatePassiveSellOffer operation: Field: Selling, Error: asset code length must be between 1 and 12 characters` - assert.Contains(t, err.Error(), expected) - } -} - -func TestCreatePassiveSellOfferValidateAmount(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761100)) - - createPassiveOffer := CreatePassiveSellOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "-3", - Price: "1.0", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createPassiveOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := `validation failed for *txnbuild.CreatePassiveSellOffer operation: Field: Amount, Error: amount can not be negative` - assert.Contains(t, err.Error(), expected) - } -} - -func TestCreatePassiveSellOfferValidatePrice(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761100)) - - createPassiveOffer := CreatePassiveSellOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "3", - Price: "-1.0", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createPassiveOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := `validation failed for *txnbuild.CreatePassiveSellOffer operation: Field: Price, Error: amount can not be negative` - assert.Contains(t, err.Error(), expected) - } -} - -func TestCreatePassiveSellOfferPrice(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(41137196761100)) - - offer := CreatePassiveSellOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "1", - Price: "0.000000001", - SourceAccount: &sourceAccount, - } - - xdrOp, err := offer.BuildXDR() - assert.NoError(t, err) - expectedPrice := xdr.Price{N: 1, D: 1000000000} - assert.Equal(t, expectedPrice, xdrOp.Body.CreatePassiveSellOfferOp.Price) - assert.Equal(t, offer.Price, offer.price.string()) - assert.Equal(t, expectedPrice, offer.price.toXDR()) - - parsed := CreatePassiveSellOffer{} - assert.NoError(t, parsed.FromXDR(xdrOp)) - assert.Equal(t, offer.Price, parsed.Price) - assert.Equal(t, offer.price, parsed.price) -} diff --git a/services/horizon/internal/txnbuild/example_test.go b/services/horizon/internal/txnbuild/example_test.go deleted file mode 100644 index 0a36e78ff4..0000000000 --- a/services/horizon/internal/txnbuild/example_test.go +++ /dev/null @@ -1,769 +0,0 @@ -package txnbuild - -import ( - "fmt" - "time" - - "github.com/stellar/go/keypair" - "github.com/stellar/go/network" - horizonclient "github.com/stellar/go/txnbuild/examplehorizonclient" -) - -func ExampleInflation() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := Inflation{} - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAHqLnLFAAAAQP3NHWXvzKIHB3+jjhHITdc/tBPntWYj3SoTjpON+dxjKqU5ohFamSHeqi5ONXkhE9Uajr5sVZXjQfUcTTzsWAA= -} - -func ExampleCreateAccount() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABAqyuXG3pGL9a4MZwrX5OTWF1gd094rsowh2zXSZzDPDoGlAVljE/yjo7p6MkUY7TpMAa3Y+iXC5ael6JVD0pyDQ== -} - -func ExamplePayment() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := Payment{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - Asset: NativeAsset{}, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAAAAAAF9eEAAAAAAAAAAAHqLnLFAAAAQHb8LTro4QVpzcGzOToW28p340o54KX5/xxodABM+izweQlbVKb9bISRUOu+sNfi50weXeAeGVL+oTQS5YR4lgI= -} - -func ExamplePayment_setBaseFee() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op1 := Payment{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - Asset: NativeAsset{}, - } - - op2 := Payment{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "100", - Asset: NativeAsset{}, - } - - // get fees from network - feeStats, err := client.FeeStats() - check(err) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op1, &op2}, - BaseFee: feeStats.MaxFee.P50, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAEsAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAABAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAAAAAAF9eEAAAAAAAAAAAEAAAAAhODe2rwbSS+e+giFk8xgmxj70pVXzEADo3GG0rEhdlQAAAAAAAAAADuaygAAAAAAAAAAAeoucsUAAABAyY5c/6T3cQ1i27t681O7aHrdSQ2tCcXpyLj06HVe59DeuHNLgN3X7oBeqBZrgVty+VNVGPEK6uR+UjhGi/bGBA== -} - -func ExampleBumpSequence() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := BumpSequence{ - BumpTo: 9606132444168300, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAALACIgugAAAGwAAAAAAAAAAeoucsUAAABAQi/I4d0+fzZyQpchIYXqxHhhTmjHvfmK8qsL/BLjrXmPUADja9tdIupKEkDn/v8NfnpRS/4u3u+Vy70zuOxHDg== -} - -func ExampleAccountMerge() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := AccountMerge{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAIAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAAAAAHqLnLFAAAAQC87HdYfOZpOx/isr7JEOy9ef3GH51ToKSkC6b4UJdDktlCqHFCD0cSttJ/F5MUx2ScSkwpeAlEVR8B62X6N/g4= -} - -func ExampleManageData() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := ManageData{ - Name: "Fruit preference", - Value: []byte("Apple"), - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAABAAAABUFwcGxlAAAAAAAAAAAAAAHqLnLFAAAAQO1ELJBEoqBDyIsS7uSJwe1LOimV/E+09MyF1G/+yrxSggFVPEjD5LXcm/6POze3IsMuIYJU1et5Q2Vt9f73zQo= -} - -func ExampleManageData_removeDataEntry() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := ManageData{ - Name: "Fruit preference", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAAAAAAAAAAAAAHqLnLFAAAAQMWkjW+mHMbwOfLhpUMDu3I6U/nv132RY7RT++arqlZOs2hx3r7FOJTvndbnSSwSxwDp/VY3BSxB/4MLCZl+ogA= -} - -func ExampleSetOptions() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := SetOptions{ - InflationDestination: NewInflationDestination("GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z"), - ClearFlags: []AccountFlag{AuthRevocable}, - SetFlags: []AccountFlag{AuthRequired, AuthImmutable}, - MasterWeight: NewThreshold(10), - LowThreshold: NewThreshold(1), - MediumThreshold: NewThreshold(2), - HighThreshold: NewThreshold(2), - HomeDomain: NewHomeDomain("LovelyLumensLookLuminous.com"), - Signer: &Signer{Address: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", Weight: Threshold(4)}, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAQAAAACE4N7avBtJL576CIWTzGCbGPvSlVfMQAOjcYbSsSF2VAAAAAEAAAACAAAAAQAAAAUAAAABAAAACgAAAAEAAAABAAAAAQAAAAIAAAABAAAAAgAAAAEAAAAcTG92ZWx5THVtZW5zTG9va0x1bWlub3VzLmNvbQAAAAEAAAAAhODe2rwbSS+e+giFk8xgmxj70pVXzEADo3GG0rEhdlQAAAAEAAAAAAAAAAHqLnLFAAAAQHGdxG4uiB41Dywb1OiNQwHpCYoNZiaEXTRbPjdRf3SkBCdI1wkBDG6vREDsWfouMks5urKNx0hzg/YMLTa7TwY= -} - -func ExampleChangeTrust() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := ChangeTrust{ - Line: CreditAsset{"ABCD", "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z"}, - Limit: "10", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAAhODe2rwbSS+e+giFk8xgmxj70pVXzEADo3GG0rEhdlQAAAAABfXhAAAAAAAAAAAB6i5yxQAAAECqpS4iUUyuUSVicZIseVoj8DjWgYDet21zUQeHNr1teTflnCUS+awFQ5lNqxl+AHPB34JzN6RYoEISoEIfNpIH -} - -func ExampleChangeTrust_removeTrustline() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := RemoveTrustlineOp(CreditAsset{"ABCD", "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z"}) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAAhODe2rwbSS+e+giFk8xgmxj70pVXzEADo3GG0rEhdlQAAAAAAAAAAAAAAAAAAAAB6i5yxQAAAEAouZRZwuPF5j68byMRcw2mtToS6nFsxGJcZjO4oGm2dWVsVS1MGqFhr+JvIJlMRUKKdPxtZAoO9kjSbpUspUcC -} - -func ExampleAllowTrust() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := AllowTrust{ - Trustor: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Type: CreditAsset{"ABCD", "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z"}, - Authorize: true, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAHAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAUFCQ0QAAAABAAAAAAAAAAHqLnLFAAAAQBjcydaIxwvXxLFEhNK4jm1lJeYSjRDfxRmDSOIkZTZTqRKewI1NMmIYAIZCUis98Axi32ShqutfXXDscsGixA0= -} - -func ExampleManageSellOffer() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP"} - sellAmount := "100" - price := "0.01" - op, err := CreateOfferOp(selling, buying, sellAmount, price) - check(err) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAAHqLnLFAAAAQG1+s35VQTuILAGTT6uaDT9RrgMi0xYTLqdoZbGgMGLiSwIglJk/OS/v1DrmshoXIhwL/O7Ilychy/vcA/4dAQo= -} - -func ExampleManageSellOffer_deleteOffer() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - offerID := int64(2921622) - op, err := DeleteOfferOp(offerID) - check(err) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFGQUtFAAAAAEEHgGTElYZi82AkGiJdSja2OBaU2aEcwwp3AY3tFJ2xAAAAAAAAAAAAAAABAAAAAQAAAAAALJSWAAAAAAAAAAHqLnLFAAAAQGcT6ggtq6q3qbx+PsMgE1b9cGYonfhIu8d3E/Ti9vbpojyr2L/an3+kkydY946gjDR/qOt5HfTqo8kWGMy2XgY= -} - -func ExampleManageSellOffer_updateOffer() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP"} - sellAmount := "50" - price := "0.02" - offerID := int64(2497628) - op, err := UpdateOfferOp(selling, buying, sellAmount, price, offerID) - check(err) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAB3NZQAAAAABAAAAMgAAAAAAJhxcAAAAAAAAAAHqLnLFAAAAQKY77jK6QC4tG1HghFY9W2jJnYsl5qKk+55z78zUkYOhMU9QsOXeSC6A/BXeavSO8w0CsF1HxLc1TDfWC1PlNw4= -} - -func ExampleCreatePassiveSellOffer() { - kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - op := CreatePassiveSellOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP"}, - Amount: "10", - Price: "1.0", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAX14QAAAAABAAAAAQAAAAAAAAAB6i5yxQAAAEAThdst0NXPUzAL0GzzieSoryHIeF5VtjOc1KIA/SGI/xq69woAydjPccm/MzwfSr8rkw++AFp6Edn+1C1o9IYG -} - -func ExamplePathPayment() { - kp, _ := keypair.Parse("SBZVMB74Z76QZ3ZOY7UTDFYKMEGKW5XFJEB6PFKBF4UYSSWHG4EDH7PY") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - abcdAsset := CreditAsset{"ABCD", "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3"} - op := PathPayment{ - SendAsset: NativeAsset{}, - SendMax: "10", - Destination: kp.Address(), - DestAsset: NativeAsset{}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAF9eEAAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAAmJaAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAEuFVmYAAAAQOGE+w2bvIp8JQIPIFXWk5kO77cNUOlPZwlItA5V68/qmZTbJWq8wqdZtjELkZtNcQQX4x8EToShbn5nitG3RA4= -} - -func ExamplePathPaymentStrictReceive() { - kp, _ := keypair.Parse("SBZVMB74Z76QZ3ZOY7UTDFYKMEGKW5XFJEB6PFKBF4UYSSWHG4EDH7PY") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - abcdAsset := CreditAsset{"ABCD", "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3"} - op := PathPaymentStrictReceive{ - SendAsset: NativeAsset{}, - SendMax: "10", - Destination: kp.Address(), - DestAsset: NativeAsset{}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAF9eEAAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAAmJaAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAEuFVmYAAAAQOGE+w2bvIp8JQIPIFXWk5kO77cNUOlPZwlItA5V68/qmZTbJWq8wqdZtjELkZtNcQQX4x8EToShbn5nitG3RA4= -} - -func ExamplePathPaymentStrictSend() { - kp, _ := keypair.Parse("SBZVMB74Z76QZ3ZOY7UTDFYKMEGKW5XFJEB6PFKBF4UYSSWHG4EDH7PY") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - abcdAsset := CreditAsset{"ABCD", "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3"} - op := PathPaymentStrictSend{ - SendAsset: NativeAsset{}, - SendAmount: "1", - Destination: kp.Address(), - DestAsset: NativeAsset{}, - DestMin: "10", - Path: []Asset{abcdAsset}, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAANAAAAAAAAAAAAmJaAAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAF9eEAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAEuFVmYAAAAQNXoKZHgBO+2baoHMcT1SqpL3lmcggeCm5TuFNk7fwMeF/6h5lDTYMa+y3i9gwwAg8aQwCwuV8A38AWKfPvgMAM= -} - -func ExampleManageBuyOffer() { - kp, _ := keypair.Parse("SBZVMB74Z76QZ3ZOY7UTDFYKMEGKW5XFJEB6PFKBF4UYSSWHG4EDH7PY") - client := horizonclient.DefaultTestNetClient - ar := horizonclient.AccountRequest{AccountID: kp.Address()} - sourceAccount, err := client.AccountDetail(ar) - check(err) - - buyOffer := ManageBuyOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3"}, - Amount: "100", - Price: "0.01", - OfferID: 0, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! - }, - ) - check(err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) - check(err) - - txe, err := tx.Base64() - check(err) - fmt.Println(txe) - - // Output: AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAAEuFVmYAAAAQPh8h1TrzDpcgzB/VE8V0X2pFGV8/JyuYrx0I5bRfBJuLJr0l8yL1isP1wZjvMdX7fNiktwSLuUuj749nWA6wAo= - -} - -// Action needed in release: horizonclient-v3.2.0 -// Uncomment this example when protocol 13 is enabled -//func ExampleFeeBumpTransaction() { -// kp, _ := keypair.Parse("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") -// client := horizonclient.DefaultTestNetClient -// ar := horizonclient.AccountRequest{AccountID: kp.Address()} -// sourceAccount, err := client.AccountDetail(ar) -// check(err) -// -// op := BumpSequence{ -// BumpTo: 9606132444168300, -// } -// -// tx, err := NewTransaction( -// TransactionParams{ -// SourceAccount: &sourceAccount, -// IncrementSequenceNum: true, -// Operations: []Operation{&op}, -// BaseFee: MinBaseFee, -// Timebounds: NewInfiniteTimeout(), // Use a real timeout in production! -// }, -// ) -// check(err) -// tx, err = tx.Sign(network.TestNetworkPassphrase, kp.(*keypair.Full)) -// check(err) -// -// feeBumpKP, _ := keypair.Parse("SBZVMB74Z76QZ3ZOY7UTDFYKMEGKW5XFJEB6PFKBF4UYSSWHG4EDH7PY") -// convertToV1Tx(tx) -// feeBumpTx, err := NewFeeBumpTransaction( -// FeeBumpTransactionParams{ -// Inner: tx, -// FeeAccount: feeBumpKP.Address(), -// BaseFee: MinBaseFee, -// }, -// ) -// check(err) -// feeBumpTx, err = feeBumpTx.Sign(network.TestNetworkPassphrase, feeBumpKP.(*keypair.Full)) -// check(err) -// -// txe, err := feeBumpTx.Base64() -// check(err) -// fmt.Println(txe) -// -// // Output: AAAABQAAAAB+Ecs01jX14asC1KAsPdWlpGbYCM2PEgFZCD3NLhVZmAAAAAAAAADIAAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAABAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAACwAiILoAAABsAAAAAAAAAAHqLnLFAAAAQEIvyOHdPn82ckKXISGF6sR4YU5ox735ivKrC/wS4615j1AA42vbXSLqShJA5/7/DX56UUv+Lt7vlcu9M7jsRw4AAAAAAAAAAS4VWZgAAABAeD0gL6WpzSdGTzWd4c9yUu3r+W21hOTLT4ItHGBTHYPT20Wk3dytuqfP89EzlkZXvtG8/N0HH4w+oJCLOL/5Aw== -//} - -func ExampleBuildChallengeTx() { - // Generate random nonce - serverSignerSeed := "SBZVMB74Z76QZ3ZOY7UTDFYKMEGKW5XFJEB6PFKBF4UYSSWHG4EDH7PY" - clientAccountID := "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3" - anchorName := "SDF" - timebound := time.Duration(5 * time.Minute) - - tx, err := BuildChallengeTx(serverSignerSeed, clientAccountID, anchorName, network.TestNetworkPassphrase, timebound) - check(err) - - txeBase64, err := tx.Base64() - check(err) - _, err = checkChallengeTx(txeBase64, anchorName) - - check(err) -} diff --git a/services/horizon/internal/txnbuild/fee_bump_test.go b/services/horizon/internal/txnbuild/fee_bump_test.go deleted file mode 100644 index 3ebaf0284f..0000000000 --- a/services/horizon/internal/txnbuild/fee_bump_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package txnbuild - -import ( - "encoding/base64" - "github.com/stellar/go/network" - "testing" - - "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" -) - -func TestFeeBumpMissingInner(t *testing.T) { - _, err := NewFeeBumpTransaction(FeeBumpTransactionParams{}) - assert.EqualError(t, err, "inner transaction is missing") -} - -func TestFeeBumpInvalidFeeSource(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - convertToV1Tx(tx) - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: "/.','ml", - BaseFee: MinBaseFee, - Inner: tx, - }, - ) - assert.Contains(t, err.Error(), "fee account is not a valid address") -} - -func TestFeeBumpInvalidInnerTxType(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: newKeypair1().Address(), - BaseFee: MinBaseFee, - Inner: tx, - }, - ) - assert.EqualError(t, err, "EnvelopeTypeEnvelopeTypeTxV0 transactions cannot be fee bumped") -} - -// There is a use case for having a fee bump tx where the fee account is equal to the -// source account of the inner transaction. Consider the case where the signers of the -// inner transaction could be different (which is the case when dealing with operations -// on different source accounts). -func TestFeeBumpAllowsFeeAccountToEqualInnerSourceAccount(t *testing.T) { - sourceAccount := NewSimpleAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", 1) - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - convertToV1Tx(tx) - - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: sourceAccount.AccountID, - BaseFee: MinBaseFee, - Inner: tx, - }, - ) - assert.NoError(t, err) - - muxedAccount := xdr.MuxedAccount{ - Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, - Med25519: &xdr.MuxedAccountMed25519{ - Id: 0, - Ed25519: xdr.Uint256{1, 2, 3}, - }, - } - tx.envelope.V1.Tx.SourceAccount = muxedAccount - - otherAccount := xdr.AccountId{ - Type: xdr.PublicKeyTypePublicKeyTypeEd25519, - Ed25519: &xdr.Uint256{1, 2, 3}, - } - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: otherAccount.Address(), - BaseFee: MinBaseFee, - Inner: tx, - }, - ) - assert.NoError(t, err) - - otherAccount = xdr.AccountId{ - Type: xdr.PublicKeyTypePublicKeyTypeEd25519, - Ed25519: &xdr.Uint256{1, 2, 3}, - } - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: otherAccount.Address(), - BaseFee: MinBaseFee, - Inner: tx, - }, - ) - assert.NoError(t, err) -} - -func TestFeeBumpRoundTrip(t *testing.T) { - kp0, kp1 := newKeypair0(), newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - convertToV1Tx(tx) - expectedInnerB64, err := tx.Base64() - assert.NoError(t, err) - - feeBumpTx, err := NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: kp1.Address(), - BaseFee: 2 * MinBaseFee, - Inner: tx, - }, - ) - assert.NoError(t, err) - feeBumpTx, err = feeBumpTx.Sign(network.TestNetworkPassphrase, kp1) - assert.NoError(t, err) - - innerB64, err := feeBumpTx.InnerTransaction().Base64() - assert.NoError(t, err) - assert.Equal(t, expectedInnerB64, innerB64) - - assert.Equal(t, kp1.Address(), feeBumpTx.FeeAccount()) - assert.Equal(t, int64(2*MinBaseFee), feeBumpTx.BaseFee()) - assert.Equal(t, int64(4*MinBaseFee), feeBumpTx.MaxFee()) - - env, err := feeBumpTx.TxEnvelope() - assert.NoError(t, err) - assert.Equal(t, xdr.EnvelopeTypeEnvelopeTypeTxFeeBump, env.Type) - assert.Equal(t, xdr.MustAddress(kp1.Address()), env.FeeBumpAccount().ToAccountId()) - assert.Equal(t, int64(4*MinBaseFee), env.FeeBumpFee()) - assert.Equal(t, feeBumpTx.Signatures(), env.FeeBumpSignatures()) - innerB64, err = xdr.MarshalBase64(xdr.TransactionEnvelope{ - Type: xdr.EnvelopeTypeEnvelopeTypeTx, - V1: env.FeeBump.Tx.InnerTx.V1, - }) - assert.NoError(t, err) - assert.Equal(t, expectedInnerB64, innerB64) - - expectedFeeBumpB64, err := xdr.MarshalBase64(env) - assert.NoError(t, err) - - b64, err := feeBumpTx.Base64() - assert.NoError(t, err) - assert.Equal(t, expectedFeeBumpB64, b64) - - binary, err := feeBumpTx.MarshalBinary() - assert.NoError(t, err) - assert.Equal(t, expectedFeeBumpB64, base64.StdEncoding.EncodeToString(binary)) - - parsed, err := TransactionFromXDR(expectedFeeBumpB64) - assert.NoError(t, err) - parsedFeeBump, ok := parsed.FeeBump() - assert.True(t, ok) - _, ok = parsed.Transaction() - assert.False(t, ok) - - assert.Equal(t, feeBumpTx.Signatures(), parsedFeeBump.Signatures()) - assert.Equal(t, kp1.Address(), parsedFeeBump.FeeAccount()) - assert.Equal(t, int64(2*MinBaseFee), parsedFeeBump.BaseFee()) - assert.Equal(t, int64(4*MinBaseFee), parsedFeeBump.MaxFee()) - innerB64, err = xdr.MarshalBase64(xdr.TransactionEnvelope{ - Type: xdr.EnvelopeTypeEnvelopeTypeTx, - V1: parsedFeeBump.envelope.FeeBump.Tx.InnerTx.V1, - }) - assert.NoError(t, err) - assert.Equal(t, expectedInnerB64, innerB64) - b64, err = parsedFeeBump.Base64() - assert.NoError(t, err) - assert.Equal(t, expectedFeeBumpB64, b64) -} diff --git a/services/horizon/internal/txnbuild/helpers.go b/services/horizon/internal/txnbuild/helpers.go deleted file mode 100644 index 41864b9b71..0000000000 --- a/services/horizon/internal/txnbuild/helpers.go +++ /dev/null @@ -1,194 +0,0 @@ -package txnbuild - -import ( - "fmt" - "github.com/stellar/go/amount" - "github.com/stellar/go/strkey" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// validateStellarPublicKey returns an error if a public key is invalid. Otherwise, it returns nil. -// It is a wrapper around the IsValidEd25519PublicKey method of the strkey package. -func validateStellarPublicKey(publicKey string) error { - if publicKey == "" { - return errors.New("public key is undefined") - } - - if !strkey.IsValidEd25519PublicKey(publicKey) { - return errors.Errorf("%s is not a valid stellar public key", publicKey) - } - return nil -} - -// validateStellarSignerKey returns an error if a signerkey is invalid. Otherwise, it returns nil. -func validateStellarSignerKey(signerKey string) error { - if signerKey == "" { - return errors.New("signer key is undefined") - } - - var xdrKey xdr.SignerKey - if err := xdrKey.SetAddress(signerKey); err != nil { - return errors.Errorf("%s is not a valid stellar signer key", signerKey) - } - return nil -} - -// validateStellarAsset checks if the asset supplied is a valid stellar Asset. It returns an error if the asset is -// nil, has an invalid asset code or issuer. -func validateStellarAsset(asset Asset) error { - if asset == nil { - return errors.New("asset is undefined") - } - - if asset.IsNative() { - return nil - } - - _, err := asset.GetType() - if err != nil { - return err - } - - err = validateStellarPublicKey(asset.GetIssuer()) - if err != nil { - return errors.Errorf("asset issuer: %s", err.Error()) - } - - return nil -} - -// validateAmount checks if the provided value is a valid stellar amount, it returns an error if not. -// This is used to validate price and amount fields in structs. -func validateAmount(n interface{}) error { - var stellarAmount int64 - // type switch can be extended to handle other types. Currently, the types for number values in the txnbuild - // package are string or int64. - switch value := n.(type) { - case int64: - stellarAmount = value - case string: - v, err := amount.ParseInt64(value) - if err != nil { - return err - } - stellarAmount = v - default: - return errors.Errorf("could not parse expected numeric value %v", n) - } - - if stellarAmount < 0 { - return errors.New("amount can not be negative") - } - return nil -} - -// validateAllowTrustAsset checks if the provided asset is valid for use in AllowTrust operation. -// It returns an error if the asset is invalid. -// The asset must be non native (XLM) with a valid asset code. -func validateAllowTrustAsset(asset Asset) error { - // Note: we are not using validateStellarAsset() function for AllowTrust operations because it requires the - // following : - // - asset is non-native - // - asset code is valid - // - asset issuer is not required. This is actually ignored by the operation - if asset == nil { - return errors.New("asset is undefined") - } - - if asset.IsNative() { - return errors.New("native (XLM) asset type is not allowed") - } - - _, err := asset.GetType() - if err != nil { - return err - } - return nil -} - -// validateChangeTrustAsset checks if the provided asset is valid for use in ChangeTrust operation. -// It returns an error if the asset is invalid. -// The asset must be non native (XLM) with a valid asset code and issuer. -func validateChangeTrustAsset(asset Asset) error { - // Note: we are not using validateStellarAsset() function for ChangeTrust operations because it requires the - // following : - // - asset is non-native - // - asset code is valid - // - asset issuer is valid - err := validateAllowTrustAsset(asset) - if err != nil { - return err - } - - err = validateStellarPublicKey(asset.GetIssuer()) - if err != nil { - return errors.Errorf("asset issuer: %s", err.Error()) - } - - return nil -} - -// validatePassiveOffer checks if the fields of a CreatePassiveOffer struct are valid. -// It checks that the buying and selling assets are valid stellar assets, and that amount and price are valid. -// It returns an error if any field is invalid. -func validatePassiveOffer(buying, selling Asset, offerAmount, price string) error { - // Note: see discussion on how this can be improved: - // https://github.com/stellar/go/pull/1707#discussion_r321508440 - err := validateStellarAsset(buying) - if err != nil { - return NewValidationError("Buying", err.Error()) - } - - err = validateStellarAsset(selling) - if err != nil { - return NewValidationError("Selling", err.Error()) - } - - err = validateAmount(offerAmount) - if err != nil { - return NewValidationError("Amount", err.Error()) - } - - err = validateAmount(price) - if err != nil { - return NewValidationError("Price", err.Error()) - } - - return nil -} - -// validateOffer checks if the fields of ManageBuyOffer or ManageSellOffer struct are valid. -// It checks that the buying and selling assets are valid stellar assets, and that amount, price and offerID -// are valid. It returns an error if any field is invalid. -func validateOffer(buying, selling Asset, offerAmount, price string, offerID int64) error { - err := validatePassiveOffer(buying, selling, offerAmount, price) - if err != nil { - return err - } - - err = validateAmount(offerID) - if err != nil { - return NewValidationError("OfferID", err.Error()) - } - return nil -} - -// ValidationError is a custom error struct that holds validation errors of txnbuild's operation structs. -type ValidationError struct { - Field string // Field is the struct field on which the validation error occured. - Message string // Message is the validation error message. -} - -// Error for ValidationError struct implements the error interface. -func (opError *ValidationError) Error() string { - return fmt.Sprintf("Field: %s, Error: %s", opError.Field, opError.Message) -} - -// NewValidationError creates a ValidationError struct with the provided field and message values. -func NewValidationError(field, message string) *ValidationError { - return &ValidationError{ - Field: field, - Message: message, - } -} diff --git a/services/horizon/internal/txnbuild/helpers_test.go b/services/horizon/internal/txnbuild/helpers_test.go deleted file mode 100644 index f5f5ba48bd..0000000000 --- a/services/horizon/internal/txnbuild/helpers_test.go +++ /dev/null @@ -1,358 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stellar/go/keypair" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func newKeypair0() *keypair.Full { - // Address: GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3 - return newKeypair("SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R") -} - -func newKeypair1() *keypair.Full { - // Address: GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP - return newKeypair("SBMSVD4KKELKGZXHBUQTIROWUAPQASDX7KEJITARP4VMZ6KLUHOGPTYW") -} - -func newKeypair2() *keypair.Full { - // Address: GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H - return newKeypair("SBZVMB74Z76QZ3ZOY7UTDFYKMEGKW5XFJEB6PFKBF4UYSSWHG4EDH7PY") -} - -func newKeypair(seed string) *keypair.Full { - myKeypair, _ := keypair.Parse(seed) - return myKeypair.(*keypair.Full) -} - -func check(err error) { - if err != nil { - panic(err) - } -} - -func checkChallengeTx(txeBase64, anchorName string) (bool, error) { - var txXDR xdr.TransactionEnvelope - err := xdr.SafeUnmarshalBase64(txeBase64, &txXDR) - if err != nil { - return false, err - } - op := txXDR.Operations()[0] - if (xdr.OperationTypeManageData == op.Body.Type) && (op.Body.ManageDataOp.DataName == xdr.String64(anchorName+" auth")) { - return true, nil - } - return false, errors.New("invalid challenge tx") -} - -func unmarshalBase64(txeB64 string) (xdr.TransactionEnvelope, error) { - var xdrEnv xdr.TransactionEnvelope - err := xdr.SafeUnmarshalBase64(txeB64, &xdrEnv) - return xdrEnv, err -} - -func newSignedTransaction( - params TransactionParams, - network string, - keypairs ...*keypair.Full, -) (string, error) { - tx, err := NewTransaction(params) - if err != nil { - return "", errors.Wrap(err, "couldn't create transaction") - } - - tx, err = tx.Sign(network, keypairs...) - if err != nil { - return "", errors.Wrap(err, "couldn't sign transaction") - } - - txeBase64, err := tx.Base64() - if err != nil { - return "", errors.Wrap(err, "couldn't encode transaction") - } - - return txeBase64, err -} - -func newSignedFeeBumpTransaction( - params FeeBumpTransactionParams, - network string, - keypairs ...*keypair.Full, -) (string, error) { - tx, err := NewFeeBumpTransaction(params) - if err != nil { - return "", errors.Wrap(err, "couldn't create transaction") - } - - tx, err = tx.Sign(network, keypairs...) - if err != nil { - return "", errors.Wrap(err, "couldn't sign transaction") - } - - txeBase64, err := tx.Base64() - if err != nil { - return "", errors.Wrap(err, "couldn't encode transaction") - } - - return txeBase64, err -} - -func convertToV1Tx(tx *Transaction) { - // Action needed in release: horizonclient-v3.2.0 - // remove manual envelope type configuration because - // once protocol 13 is enabled txnbuild will generate - // v1 transaction envelopes by default - tx.envelope.V1 = &xdr.TransactionV1Envelope{ - Tx: xdr.Transaction{ - SourceAccount: tx.envelope.SourceAccount(), - Fee: xdr.Uint32(tx.envelope.Fee()), - SeqNum: xdr.SequenceNumber(tx.envelope.SeqNum()), - TimeBounds: tx.envelope.V0.Tx.TimeBounds, - Memo: tx.envelope.Memo(), - Operations: tx.envelope.Operations(), - }, - } - tx.envelope.Type = xdr.EnvelopeTypeEnvelopeTypeTx - tx.envelope.V0 = nil -} - -func TestValidateStellarPublicKey(t *testing.T) { - validKey := "GDWZCOEQRODFCH6ISYQPWY67L3ULLWS5ISXYYL5GH43W7YFMTLB65PYM" - err := validateStellarPublicKey(validKey) - assert.NoError(t, err, "public key should be valid") - - invalidKey := "GDWZCOEQRODFCH6ISYQPWY67L3ULLWS5ISXYYL5GH43W7Y" - err = validateStellarPublicKey(invalidKey) - expectedErrMsg := "GDWZCOEQRODFCH6ISYQPWY67L3ULLWS5ISXYYL5GH43W7Y is not a valid stellar public key" - require.EqualError(t, err, expectedErrMsg, "public key should be invalid") - - invalidKey = "" - err = validateStellarPublicKey(invalidKey) - expectedErrMsg = "public key is undefined" - require.EqualError(t, err, expectedErrMsg, "public key should be invalid") - - invalidKey = "SBCVMMCBEDB64TVJZFYJOJAERZC4YVVUOE6SYR2Y76CBTENGUSGWRRVO" - err = validateStellarPublicKey(invalidKey) - expectedErrMsg = "SBCVMMCBEDB64TVJZFYJOJAERZC4YVVUOE6SYR2Y76CBTENGUSGWRRVO is not a valid stellar public key" - require.EqualError(t, err, expectedErrMsg, "public key should be invalid") -} - -func TestValidateStellarAssetWithValidAsset(t *testing.T) { - nativeAsset := NativeAsset{} - err := validateStellarAsset(nativeAsset) - assert.NoError(t, err) - - kp0 := newKeypair0() - creditAsset := CreditAsset{"XYZ", kp0.Address()} - err = validateStellarAsset(creditAsset) - assert.NoError(t, err) -} - -func TestValidateStellarAssetWithInValidAsset(t *testing.T) { - err := validateStellarAsset(nil) - assert.Error(t, err) - expectedErrMsg := "asset is undefined" - require.EqualError(t, err, expectedErrMsg, "An asset is required") - - kp0 := newKeypair0() - creditAssetNoCode := CreditAsset{Code: "", Issuer: kp0.Address()} - err = validateStellarAsset(creditAssetNoCode) - assert.Error(t, err) - expectedErrMsg = "asset code length must be between 1 and 12 characters" - require.EqualError(t, err, expectedErrMsg, "An asset code is required") - - creditAssetNoIssuer := CreditAsset{Code: "ABC", Issuer: ""} - err = validateStellarAsset(creditAssetNoIssuer) - assert.Error(t, err) - expectedErrMsg = "asset issuer: public key is undefined" - require.EqualError(t, err, expectedErrMsg, "An asset issuer is required") -} - -func TestValidateAmount(t *testing.T) { - err := validateAmount(int64(10)) - assert.NoError(t, err) - - err = validateAmount("10") - assert.NoError(t, err) - - err = validateAmount(int64(0)) - assert.NoError(t, err) - - err = validateAmount("0") - assert.NoError(t, err) -} - -func TestValidateAmountInvalidValue(t *testing.T) { - err := validateAmount(int64(-10)) - assert.Error(t, err) - expectedErrMsg := "amount can not be negative" - require.EqualError(t, err, expectedErrMsg, "should be a valid stellar amount") - - err = validateAmount("-10") - assert.Error(t, err) - expectedErrMsg = "amount can not be negative" - require.EqualError(t, err, expectedErrMsg, "should be a valid stellar amount") - - err = validateAmount(10) - assert.Error(t, err) - expectedErrMsg = "could not parse expected numeric value 10" - require.EqualError(t, err, expectedErrMsg, "should be a valid stellar amount") - - err = validateAmount("abc") - assert.Error(t, err) - expectedErrMsg = "invalid amount format: abc" - require.EqualError(t, err, expectedErrMsg, "should be a valid stellar amount") -} - -func TestValidateAllowTrustAsset(t *testing.T) { - err := validateAllowTrustAsset(nil) - assert.Error(t, err) - expectedErrMsg := "asset is undefined" - require.EqualError(t, err, expectedErrMsg, "An asset is required") - - err = validateAllowTrustAsset(NativeAsset{}) - assert.Error(t, err) - expectedErrMsg = "native (XLM) asset type is not allowed" - require.EqualError(t, err, expectedErrMsg, "An asset is required") - - // allow trust asset does not require asset issuer - atAsset := CreditAsset{Code: "ABCD"} - err = validateAllowTrustAsset(atAsset) - assert.NoError(t, err) -} - -func TestValidateChangeTrustAsset(t *testing.T) { - err := validateChangeTrustAsset(nil) - assert.Error(t, err) - expectedErrMsg := "asset is undefined" - require.EqualError(t, err, expectedErrMsg, "An asset is required") - - err = validateChangeTrustAsset(NativeAsset{}) - assert.Error(t, err) - expectedErrMsg = "native (XLM) asset type is not allowed" - require.EqualError(t, err, expectedErrMsg, "A custom asset is required") - - kp0 := newKeypair0() - ctAsset0 := CreditAsset{Issuer: kp0.Address()} - err = validateChangeTrustAsset(ctAsset0) - assert.Error(t, err) - expectedErrMsg = "asset code length must be between 1 and 12 characters" - require.EqualError(t, err, expectedErrMsg, "asset code is required") - - ctAsset1 := CreditAsset{Code: "ABCD"} - err = validateChangeTrustAsset(ctAsset1) - assert.Error(t, err) - expectedErrMsg = "asset issuer: public key is undefined" - require.EqualError(t, err, expectedErrMsg, "asset issuer is required") - - ctAsset2 := CreditAsset{Code: "ABCD", Issuer: kp0.Address()} - err = validateChangeTrustAsset(ctAsset2) - assert.NoError(t, err) -} - -func TestValidatePassiveOfferZeroValues(t *testing.T) { - cpo := CreatePassiveSellOffer{} - err := validatePassiveOffer(cpo.Buying, cpo.Selling, cpo.Amount, cpo.Price) - assert.Error(t, err) - expectedErrMsg := "Field: Buying, Error: asset is undefined" - require.EqualError(t, err, expectedErrMsg, "Buying asset is required") -} - -func TestValidatePassiveOfferInvalidAmount(t *testing.T) { - kp0 := newKeypair0() - buying := CreditAsset{Code: "ABCD", Issuer: kp0.Address()} - selling := NativeAsset{} - cpo := CreatePassiveSellOffer{ - Buying: buying, - Selling: selling, - Price: "1", - Amount: "-1", - } - err := validatePassiveOffer(cpo.Buying, cpo.Selling, cpo.Amount, cpo.Price) - assert.Error(t, err) - expectedErrMsg := "Field: Amount, Error: amount can not be negative" - require.EqualError(t, err, expectedErrMsg, "valid amount is required") -} - -func TestValidatePassiveOfferInvalidPrice(t *testing.T) { - kp0 := newKeypair0() - buying := CreditAsset{Code: "ABCD", Issuer: kp0.Address()} - selling := NativeAsset{} - cpo := CreatePassiveSellOffer{ - Buying: buying, - Selling: selling, - Price: "-1", - Amount: "10", - } - err := validatePassiveOffer(cpo.Buying, cpo.Selling, cpo.Amount, cpo.Price) - assert.Error(t, err) - expectedErrMsg := "Field: Price, Error: amount can not be negative" - require.EqualError(t, err, expectedErrMsg, "valid price is required") -} - -func TestValidatePassiveOfferInvalidAsset(t *testing.T) { - buying := NativeAsset{} - selling := CreditAsset{Code: "ABCD"} - cpo := CreatePassiveSellOffer{ - Buying: buying, - Selling: selling, - Price: "1", - Amount: "10", - } - err := validatePassiveOffer(cpo.Buying, cpo.Selling, cpo.Amount, cpo.Price) - assert.Error(t, err) - expectedErrMsg := "Field: Selling, Error: asset issuer: public key is undefined" - require.EqualError(t, err, expectedErrMsg, "Selling asset is required") - - kp0 := newKeypair0() - buying1 := CreditAsset{Issuer: kp0.Address()} - selling1 := NativeAsset{} - cpo1 := CreatePassiveSellOffer{ - Buying: buying1, - Selling: selling1, - Price: "1", - Amount: "10", - } - err = validatePassiveOffer(cpo1.Buying, cpo1.Selling, cpo1.Amount, cpo1.Price) - assert.Error(t, err) - expectedErrMsg = "Field: Buying, Error: asset code length must be between 1 and 12 characters" - require.EqualError(t, err, expectedErrMsg, "Selling asset is required") -} - -func TestValidateOfferManageBuyOffer(t *testing.T) { - kp0 := newKeypair0() - buying := CreditAsset{Code: "ABCD", Issuer: kp0.Address()} - selling := NativeAsset{} - mbo := ManageBuyOffer{ - Buying: buying, - Selling: selling, - Price: "1", - Amount: "10", - OfferID: -1, - } - err := validateOffer(mbo.Buying, mbo.Selling, mbo.Amount, mbo.Price, mbo.OfferID) - assert.Error(t, err) - expectedErrMsg := "Field: OfferID, Error: amount can not be negative" - require.EqualError(t, err, expectedErrMsg, "valid offerID is required") -} - -func TestValidateOfferManageSellOffer(t *testing.T) { - kp0 := newKeypair0() - buying := CreditAsset{Code: "ABCD", Issuer: kp0.Address()} - selling := NativeAsset{} - mso := ManageSellOffer{ - Buying: buying, - Selling: selling, - Price: "1", - Amount: "10", - OfferID: -1, - } - err := validateOffer(mso.Buying, mso.Selling, mso.Amount, mso.Price, mso.OfferID) - assert.Error(t, err) - expectedErrMsg := "Field: OfferID, Error: amount can not be negative" - require.EqualError(t, err, expectedErrMsg, "valid offerID is required") -} diff --git a/services/horizon/internal/txnbuild/inflation.go b/services/horizon/internal/txnbuild/inflation.go deleted file mode 100644 index df5b458f34..0000000000 --- a/services/horizon/internal/txnbuild/inflation.go +++ /dev/null @@ -1,46 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// Inflation represents the Stellar inflation operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type Inflation struct { - SourceAccount Account -} - -// BuildXDR for Inflation returns a fully configured XDR Operation. -func (inf *Inflation) BuildXDR() (xdr.Operation, error) { - opType := xdr.OperationTypeInflation - body, err := xdr.NewOperationBody(opType, nil) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, inf.SourceAccount) - return op, nil -} - -// FromXDR for Inflation initialises the txnbuild struct from the corresponding xdr Operation. -func (inf *Inflation) FromXDR(xdrOp xdr.Operation) error { - if xdrOp.Body.Type != xdr.OperationTypeInflation { - return errors.New("error parsing inflation operation from xdr") - } - inf.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - return nil -} - -// Validate for Inflation is just a method that implements the Operation interface. No logic is actually performed -// because the inflation operation does not have any required field. Nil is always returned. -func (inf *Inflation) Validate() error { - // no required fields, return nil. - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (inf *Inflation) GetSourceAccount() Account { - return inf.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/manage_buy_offer.go b/services/horizon/internal/txnbuild/manage_buy_offer.go deleted file mode 100644 index 877bea8e7c..0000000000 --- a/services/horizon/internal/txnbuild/manage_buy_offer.go +++ /dev/null @@ -1,98 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// ManageBuyOffer represents the Stellar manage buy offer operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type ManageBuyOffer struct { - Selling Asset - Buying Asset - Amount string - Price string - price price - OfferID int64 - SourceAccount Account -} - -// BuildXDR for ManageBuyOffer returns a fully configured XDR Operation. -func (mo *ManageBuyOffer) BuildXDR() (xdr.Operation, error) { - xdrSelling, err := mo.Selling.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") - } - - xdrBuying, err := mo.Buying.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Buying' field") - } - - xdrAmount, err := amount.Parse(mo.Amount) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse 'Amount'") - } - - if err = mo.price.parse(mo.Price); err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse 'Price'") - } - - opType := xdr.OperationTypeManageBuyOffer - xdrOp := xdr.ManageBuyOfferOp{ - Selling: xdrSelling, - Buying: xdrBuying, - BuyAmount: xdrAmount, - Price: mo.price.toXDR(), - OfferId: xdr.Int64(mo.OfferID), - } - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, mo.SourceAccount) - return op, nil -} - -// FromXDR for ManageBuyOffer initialises the txnbuild struct from the corresponding xdr Operation. -func (mo *ManageBuyOffer) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetManageBuyOfferOp() - if !ok { - return errors.New("error parsing manage_buy_offer operation from xdr") - } - - mo.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - mo.OfferID = int64(result.OfferId) - mo.Amount = amount.String(result.BuyAmount) - if result.Price != (xdr.Price{}) { - mo.price.fromXDR(result.Price) - mo.Price = mo.price.string() - } - buyingAsset, err := assetFromXDR(result.Buying) - if err != nil { - return errors.Wrap(err, "error parsing buying_asset in manage_buy_offer operation") - } - mo.Buying = buyingAsset - - sellingAsset, err := assetFromXDR(result.Selling) - if err != nil { - return errors.Wrap(err, "error parsing selling_asset in manage_buy_offer operation") - } - mo.Selling = sellingAsset - return nil -} - -// Validate for ManageBuyOffer validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (mo *ManageBuyOffer) Validate() error { - return validateOffer(mo.Buying, mo.Selling, mo.Amount, mo.Price, mo.OfferID) -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (mo *ManageBuyOffer) GetSourceAccount() Account { - return mo.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/manage_buy_offer_test.go b/services/horizon/internal/txnbuild/manage_buy_offer_test.go deleted file mode 100644 index 3eecbb49fc..0000000000 --- a/services/horizon/internal/txnbuild/manage_buy_offer_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/xdr" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManageBuyOfferValidateSellingAsset(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - buyOffer := ManageBuyOffer{ - Selling: CreditAsset{"", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "100", - Price: "0.01", - OfferID: 0, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageBuyOffer operation: Field: Selling, Error: asset code length must be between 1 and 12 characters" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageBuyOfferValidateBuyingAsset(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - buyOffer := ManageBuyOffer{ - Selling: CreditAsset{"ABC", kp0.Address()}, - Buying: CreditAsset{"XYZ", ""}, - Amount: "100", - Price: "0.01", - OfferID: 0, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageBuyOffer operation: Field: Buying, Error: asset issuer: public key is undefined" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageBuyOfferValidateAmount(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - buyOffer := ManageBuyOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "", - Price: "0.01", - OfferID: 0, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageBuyOffer operation: Field: Amount, Error: invalid amount format:" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageBuyOfferValidatePrice(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - buyOffer := ManageBuyOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "0", - Price: "-0.01", - OfferID: 0, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageBuyOffer operation: Field: Price, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageBuyOfferValidateOfferID(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - buyOffer := ManageBuyOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "0", - Price: "0.01", - OfferID: -1, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageBuyOffer operation: Field: OfferID, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageBuyOfferPrice(t *testing.T) { - kp0 := newKeypair0() - - mbo := ManageBuyOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "1", - Price: "0.000000001", - OfferID: 1, - } - - xdrOp, err := mbo.BuildXDR() - assert.NoError(t, err) - expectedPrice := xdr.Price{N: 1, D: 1000000000} - assert.Equal(t, expectedPrice, xdrOp.Body.ManageBuyOfferOp.Price) - assert.Equal(t, mbo.Price, mbo.price.string()) - assert.Equal(t, expectedPrice, mbo.price.toXDR()) - - parsed := ManageBuyOffer{} - assert.NoError(t, parsed.FromXDR(xdrOp)) - assert.Equal(t, mbo.Price, parsed.Price) - assert.Equal(t, mbo.price, parsed.price) -} diff --git a/services/horizon/internal/txnbuild/manage_data.go b/services/horizon/internal/txnbuild/manage_data.go deleted file mode 100644 index decfbee934..0000000000 --- a/services/horizon/internal/txnbuild/manage_data.go +++ /dev/null @@ -1,72 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// ManageData represents the Stellar manage data operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type ManageData struct { - Name string - Value []byte - SourceAccount Account -} - -// BuildXDR for ManageData returns a fully configured XDR Operation. -func (md *ManageData) BuildXDR() (xdr.Operation, error) { - xdrOp := xdr.ManageDataOp{DataName: xdr.String64(md.Name)} - - // No data value clears the named data entry on the account - if md.Value == nil { - xdrOp.DataValue = nil - } else { - xdrDV := xdr.DataValue(md.Value) - xdrOp.DataValue = &xdrDV - } - - opType := xdr.OperationTypeManageData - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, md.SourceAccount) - return op, nil -} - -// FromXDR for ManageData initialises the txnbuild struct from the corresponding xdr Operation. -func (md *ManageData) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetManageDataOp() - if !ok { - return errors.New("error parsing create_account operation from xdr") - } - - md.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - md.Name = string(result.DataName) - if result.DataValue != nil { - md.Value = *result.DataValue - } else { - md.Value = nil - } - return nil -} - -// Validate for ManageData validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (md *ManageData) Validate() error { - if len(md.Name) > 64 { - return NewValidationError("Name", "maximum length is 64 characters") - } - - if len(md.Value) > 64 { - return NewValidationError("Value", "maximum length is 64 bytes") - } - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (md *ManageData) GetSourceAccount() Account { - return md.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/manage_data_test.go b/services/horizon/internal/txnbuild/manage_data_test.go deleted file mode 100644 index 79b7e529ca..0000000000 --- a/services/horizon/internal/txnbuild/manage_data_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" -) - -func TestManageDataValidateName(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(3556091187167235)) - - manageData := ManageData{ - Name: "This is a very long name for a field that only accepts 64 characters", - Value: []byte(""), - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&manageData}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageData operation: Field: Name, Error: maximum length is 64 characters" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageDataValidateValue(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(3556091187167235)) - - manageData := ManageData{ - Name: "cars", - Value: []byte("toyota, ford, porsche, lamborghini, hyundai, volkswagen, gmc, kia"), - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&manageData}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageData operation: Field: Value, Error: maximum length is 64 bytes" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageDataRoundTrip(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(3556091187167235)) - - for _, testCase := range []struct { - name string - value []byte - }{ - { - "nil data", - nil, - }, - { - "empty data slice", - []byte{}, - }, - { - "non-empty data slice", - []byte{1, 2, 3}, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - manageData := ManageData{ - Name: "key", - Value: testCase.value, - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&manageData}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - envelope, err := tx.TxEnvelope() - assert.NoError(t, err) - assert.Len(t, envelope.Operations(), 1) - assert.Equal(t, xdr.String64(manageData.Name), envelope.Operations()[0].Body.ManageDataOp.DataName) - if testCase.value == nil { - assert.Nil(t, envelope.Operations()[0].Body.ManageDataOp.DataValue) - } else { - assert.Len(t, []byte(*envelope.Operations()[0].Body.ManageDataOp.DataValue), len(testCase.value)) - if len(testCase.value) > 0 { - assert.Equal(t, testCase.value, []byte(*envelope.Operations()[0].Body.ManageDataOp.DataValue)) - } - } - - txe, err := tx.Base64() - if err != nil { - assert.NoError(t, err) - } - - parsed, err := TransactionFromXDR(txe) - assert.NoError(t, err) - - tx, _ = parsed.Transaction() - - assert.Len(t, tx.Operations(), 1) - op := tx.Operations()[0].(*ManageData) - assert.Equal(t, manageData.Name, op.Name) - assert.Len(t, op.Value, len(manageData.Value)) - if len(manageData.Value) > 0 { - assert.Equal(t, manageData.Value, op.Value) - } - }) - } -} diff --git a/services/horizon/internal/txnbuild/manage_offer.go b/services/horizon/internal/txnbuild/manage_offer.go deleted file mode 100644 index 6b0a1c12fa..0000000000 --- a/services/horizon/internal/txnbuild/manage_offer.go +++ /dev/null @@ -1,162 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -//CreateOfferOp returns a ManageSellOffer operation to create a new offer, by -// setting the OfferID to "0". The sourceAccount is optional, and if not provided, -// will be that of the surrounding transaction. -func CreateOfferOp(selling, buying Asset, amount, price string, sourceAccount ...Account) (ManageSellOffer, error) { - if len(sourceAccount) > 1 { - return ManageSellOffer{}, errors.New("offer can't have multiple source accounts") - } - offer := ManageSellOffer{ - Selling: selling, - Buying: buying, - Amount: amount, - Price: price, - OfferID: 0, - } - if len(sourceAccount) == 1 { - offer.SourceAccount = sourceAccount[0] - } - return offer, nil -} - -// UpdateOfferOp returns a ManageSellOffer operation to update an offer. -// The sourceAccount is optional, and if not provided, will be that of -// the surrounding transaction. -func UpdateOfferOp(selling, buying Asset, amount, price string, offerID int64, sourceAccount ...Account) (ManageSellOffer, error) { - if len(sourceAccount) > 1 { - return ManageSellOffer{}, errors.New("offer can't have multiple source accounts") - } - offer := ManageSellOffer{ - Selling: selling, - Buying: buying, - Amount: amount, - Price: price, - OfferID: offerID, - } - if len(sourceAccount) == 1 { - offer.SourceAccount = sourceAccount[0] - } - return offer, nil -} - -//DeleteOfferOp returns a ManageSellOffer operation to delete an offer, by -// setting the Amount to "0". The sourceAccount is optional, and if not provided, -// will be that of the surrounding transaction. -func DeleteOfferOp(offerID int64, sourceAccount ...Account) (ManageSellOffer, error) { - // It turns out Stellar core doesn't care about any of these fields except the amount. - // However, Horizon will reject ManageSellOffer if it is missing fields. - // Horizon will also reject if Buying == Selling. - // Therefore unfortunately we have to make up some dummy values here. - if len(sourceAccount) > 1 { - return ManageSellOffer{}, errors.New("offer can't have multiple source accounts") - } - offer := ManageSellOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{Code: "FAKE", Issuer: "GBAQPADEYSKYMYXTMASBUIS5JI3LMOAWSTM2CHGDBJ3QDDPNCSO3DVAA"}, - Amount: "0", - Price: "1", - OfferID: offerID, - } - if len(sourceAccount) == 1 { - offer.SourceAccount = sourceAccount[0] - } - return offer, nil -} - -// ManageSellOffer represents the Stellar manage offer operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type ManageSellOffer struct { - Selling Asset - Buying Asset - Amount string - Price string - price price - OfferID int64 - SourceAccount Account -} - -// BuildXDR for ManageSellOffer returns a fully configured XDR Operation. -func (mo *ManageSellOffer) BuildXDR() (xdr.Operation, error) { - xdrSelling, err := mo.Selling.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Selling' field") - } - - xdrBuying, err := mo.Buying.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Buying' field") - } - - xdrAmount, err := amount.Parse(mo.Amount) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse 'Amount'") - } - - if err = mo.price.parse(mo.Price); err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse 'Price'") - } - - opType := xdr.OperationTypeManageSellOffer - xdrOp := xdr.ManageSellOfferOp{ - Selling: xdrSelling, - Buying: xdrBuying, - Amount: xdrAmount, - Price: mo.price.toXDR(), - OfferId: xdr.Int64(mo.OfferID), - } - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, mo.SourceAccount) - return op, nil -} - -// FromXDR for ManageSellOffer initialises the txnbuild struct from the corresponding xdr Operation. -func (mo *ManageSellOffer) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetManageSellOfferOp() - if !ok { - return errors.New("error parsing manage_sell_offer operation from xdr") - } - - mo.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - mo.OfferID = int64(result.OfferId) - mo.Amount = amount.String(result.Amount) - if result.Price != (xdr.Price{}) { - mo.price.fromXDR(result.Price) - mo.Price = mo.price.string() - } - buyingAsset, err := assetFromXDR(result.Buying) - if err != nil { - return errors.Wrap(err, "error parsing buying_asset in manage_sell_offer operation") - } - mo.Buying = buyingAsset - - sellingAsset, err := assetFromXDR(result.Selling) - if err != nil { - return errors.Wrap(err, "error parsing selling_asset in manage_sell_offer operation") - } - mo.Selling = sellingAsset - return nil -} - -// Validate for ManageSellOffer validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (mo *ManageSellOffer) Validate() error { - return validateOffer(mo.Buying, mo.Selling, mo.Amount, mo.Price, mo.OfferID) -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (mo *ManageSellOffer) GetSourceAccount() Account { - return mo.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/manage_offer_test.go b/services/horizon/internal/txnbuild/manage_offer_test.go deleted file mode 100644 index feee18c4b7..0000000000 --- a/services/horizon/internal/txnbuild/manage_offer_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/xdr" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManageSellOfferValidateSellingAsset(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - selling := CreditAsset{"", kp0.Address()} - buying := NativeAsset{} - sellAmount := "100" - price := "0.01" - createOffer, err := CreateOfferOp(selling, buying, sellAmount, price) - check(err) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageSellOffer operation: Field: Selling, Error: asset code length must be between 1 and 12 characters" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageSellOfferValidateBuyingAsset(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - selling := NativeAsset{} - buying := CreditAsset{"", kp0.Address()} - sellAmount := "100" - price := "0.01" - createOffer, err := CreateOfferOp(selling, buying, sellAmount, price) - check(err) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageSellOffer operation: Field: Buying, Error: asset code length must be between 1 and 12 characters" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageSellOfferValidateAmount(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", kp0.Address()} - sellAmount := "-1" - price := "0.01" - createOffer, err := CreateOfferOp(selling, buying, sellAmount, price) - check(err) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageSellOffer operation: Field: Amount, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageSellOfferValidatePrice(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", kp0.Address()} - sellAmount := "0" - price := "-0.01" - createOffer, err := CreateOfferOp(selling, buying, sellAmount, price) - check(err) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&createOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageSellOffer operation: Field: Price, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageSellOfferValidateOfferID(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - mso := ManageSellOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "0", - Price: "0.01", - OfferID: -1, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&mso}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.ManageSellOffer operation: Field: OfferID, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} - -func TestManageSellOfferPrice(t *testing.T) { - kp0 := newKeypair0() - - mso := ManageSellOffer{ - Selling: CreditAsset{"ABCD", kp0.Address()}, - Buying: NativeAsset{}, - Amount: "1", - Price: "0.000000001", - OfferID: 1, - } - - xdrOp, err := mso.BuildXDR() - assert.NoError(t, err) - expectedPrice := xdr.Price{N: 1, D: 1000000000} - assert.Equal(t, expectedPrice, xdrOp.Body.ManageSellOfferOp.Price) - assert.Equal(t, mso.Price, mso.price.string()) - assert.Equal(t, expectedPrice, mso.price.toXDR()) - - parsed := ManageSellOffer{} - assert.NoError(t, parsed.FromXDR(xdrOp)) - assert.Equal(t, mso.Price, parsed.Price) - assert.Equal(t, mso.price, parsed.price) -} diff --git a/services/horizon/internal/txnbuild/memo.go b/services/horizon/internal/txnbuild/memo.go deleted file mode 100644 index 11bfe939ad..0000000000 --- a/services/horizon/internal/txnbuild/memo.go +++ /dev/null @@ -1,85 +0,0 @@ -package txnbuild - -import ( - "fmt" - - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// MemoText is used to send human messages of up to 28 bytes of ASCII/UTF-8. -type MemoText string - -// MemoID is an identifier representing the transaction originator. -type MemoID uint64 - -// MemoHash is a hash representing a reference to another transaction. -type MemoHash [32]byte - -// MemoReturn is a hash representing the hash of the transaction the sender is refunding. -type MemoReturn [32]byte - -// MemoTextMaxLength is the maximum number of bytes allowed for a text memo. -const MemoTextMaxLength = 28 - -// Memo represents the superset of all memo types. -type Memo interface { - ToXDR() (xdr.Memo, error) -} - -// ToXDR for MemoText returns an XDR object representation of a Memo of the same type. -func (mt MemoText) ToXDR() (xdr.Memo, error) { - if len(mt) > MemoTextMaxLength { - return xdr.Memo{}, fmt.Errorf("Memo text can't be longer than %d bytes", MemoTextMaxLength) - } - - return xdr.NewMemo(xdr.MemoTypeMemoText, string(mt)) -} - -// ToXDR for MemoID returns an XDR object representation of a Memo of the same type. -func (mid MemoID) ToXDR() (xdr.Memo, error) { - return xdr.NewMemo(xdr.MemoTypeMemoId, xdr.Uint64(mid)) -} - -// ToXDR for MemoHash returns an XDR object representation of a Memo of the same type. -func (mh MemoHash) ToXDR() (xdr.Memo, error) { - return xdr.NewMemo(xdr.MemoTypeMemoHash, xdr.Hash(mh)) -} - -// ToXDR for MemoReturn returns an XDR object representation of a Memo of the same type. -func (mr MemoReturn) ToXDR() (xdr.Memo, error) { - return xdr.NewMemo(xdr.MemoTypeMemoReturn, xdr.Hash(mr)) -} - -// memoFromXDR returns a Memo from XDR -func memoFromXDR(memo xdr.Memo) (Memo, error) { - var newMemo Memo - var memoCreated bool - - switch memo.Type { - case xdr.MemoTypeMemoText: - value, ok := memo.GetText() - newMemo = MemoText(value) - memoCreated = ok - case xdr.MemoTypeMemoId: - value, ok := memo.GetId() - newMemo = MemoID(uint64(value)) - memoCreated = ok - case xdr.MemoTypeMemoHash: - value, ok := memo.GetHash() - newMemo = MemoHash(value) - memoCreated = ok - case xdr.MemoTypeMemoReturn: - value, ok := memo.GetRetHash() - newMemo = MemoReturn(value) - memoCreated = ok - case xdr.MemoTypeMemoNone: - memoCreated = true - } - - if !memoCreated { - return nil, errors.New("invalid memo") - } - - return newMemo, nil -} diff --git a/services/horizon/internal/txnbuild/memo_test.go b/services/horizon/internal/txnbuild/memo_test.go deleted file mode 100644 index f4a9d9b6ce..0000000000 --- a/services/horizon/internal/txnbuild/memo_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stellar/go/xdr" - - "github.com/stretchr/testify/assert" -) - -func TestMemoFromXDR(t *testing.T) { - // memo text - xdrMemo, err := xdr.NewMemo(xdr.MemoTypeMemoText, "abc123") - assert.NoError(t, err) - memo, err := memoFromXDR(xdrMemo) - if assert.NoError(t, err) { - assert.Equal(t, MemoText("abc123"), memo, "memo text should match") - } - - // memo id - xdrMemo, err = xdr.NewMemo(xdr.MemoTypeMemoId, xdr.Uint64(1234)) - assert.NoError(t, err) - memo, err = memoFromXDR(xdrMemo) - if assert.NoError(t, err) { - assert.Equal(t, MemoID(1234), memo, "memo id should match") - } - - // memo hash - xdrMemo, err = xdr.NewMemo(xdr.MemoTypeMemoHash, xdr.Hash([32]byte{0x10})) - assert.NoError(t, err) - memo, err = memoFromXDR(xdrMemo) - if assert.NoError(t, err) { - assert.Equal(t, MemoHash([32]byte{0x10}), memo, "memo hash should match") - } - - // memo return - xdrMemo, err = xdr.NewMemo(xdr.MemoTypeMemoReturn, xdr.Hash([32]byte{0x01})) - assert.NoError(t, err) - memo, err = memoFromXDR(xdrMemo) - if assert.NoError(t, err) { - assert.Equal(t, MemoReturn([32]byte{0x01}), memo, "memo return should match") - } - - // memo none - xdrMemo, err = xdr.NewMemo(xdr.MemoTypeMemoNone, "") - assert.NoError(t, err) - memo, err = memoFromXDR(xdrMemo) - if assert.NoError(t, err) { - assert.Equal(t, nil, memo, "memo should be nil") - } -} diff --git a/services/horizon/internal/txnbuild/operation.go b/services/horizon/internal/txnbuild/operation.go deleted file mode 100644 index c847f3a5fe..0000000000 --- a/services/horizon/internal/txnbuild/operation.go +++ /dev/null @@ -1,70 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/xdr" -) - -// Operation represents the operation types of the Stellar network. -type Operation interface { - BuildXDR() (xdr.Operation, error) - FromXDR(xdrOp xdr.Operation) error - Validate() error - GetSourceAccount() Account -} - -// SetOpSourceAccount sets the source account ID on an Operation. -func SetOpSourceAccount(op *xdr.Operation, sourceAccount Account) { - if sourceAccount == nil { - return - } - var opSourceAccountID xdr.MuxedAccount - opSourceAccountID.SetAddress(sourceAccount.GetAccountID()) - op.SourceAccount = &opSourceAccountID -} - -// operationFromXDR returns a txnbuild Operation from its corresponding XDR operation -func operationFromXDR(xdrOp xdr.Operation) (Operation, error) { - var newOp Operation - switch xdrOp.Body.Type { - case xdr.OperationTypeCreateAccount: - newOp = &CreateAccount{} - case xdr.OperationTypePayment: - newOp = &Payment{} - case xdr.OperationTypePathPaymentStrictReceive: - newOp = &PathPayment{} - case xdr.OperationTypeManageSellOffer: - newOp = &ManageSellOffer{} - case xdr.OperationTypeCreatePassiveSellOffer: - newOp = &CreatePassiveSellOffer{} - case xdr.OperationTypeSetOptions: - newOp = &SetOptions{} - case xdr.OperationTypeChangeTrust: - newOp = &ChangeTrust{} - case xdr.OperationTypeAllowTrust: - newOp = &AllowTrust{} - case xdr.OperationTypeAccountMerge: - newOp = &AccountMerge{} - case xdr.OperationTypeInflation: - newOp = &Inflation{} - case xdr.OperationTypeManageData: - newOp = &ManageData{} - case xdr.OperationTypeBumpSequence: - newOp = &BumpSequence{} - case xdr.OperationTypeManageBuyOffer: - newOp = &ManageBuyOffer{} - case xdr.OperationTypePathPaymentStrictSend: - newOp = &PathPaymentStrictSend{} - } - - err := newOp.FromXDR(xdrOp) - return newOp, err -} - -// accountFromXDR returns a txnbuild Account from a XDR Account. -func accountFromXDR(account *xdr.MuxedAccount) Account { - if account != nil { - aid := account.ToAccountId() - return &SimpleAccount{AccountID: aid.Address()} - } - return nil -} diff --git a/services/horizon/internal/txnbuild/operation_test.go b/services/horizon/internal/txnbuild/operation_test.go deleted file mode 100644 index 9a5661608c..0000000000 --- a/services/horizon/internal/txnbuild/operation_test.go +++ /dev/null @@ -1,418 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stellar/go/amount" - "github.com/stellar/go/xdr" - - "github.com/stretchr/testify/assert" -) - -func TestCreateAccountFromXDR(t *testing.T) { - txeB64 := "AAAAAMOrP0B2tL9IUn5QL8nn8q88kkFui1x3oW9omCj6hLhfAAAAZAAAAMcAAAAWAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAAAAAAAAAAJ5yfHhgKAxylgecjAymWqNzLWRk/MqSYt+X9duZ2DfyAAAAF0h26AAAAAAAAAAAAvqEuF8AAABAZ5q2N2BHRylT28T1DbUVU7QKTbKZ+6DLefzJoCjHo2T8vcI/PjF8gsRu/r2M60Uzcw3WmqRFerA6DnJILIEdDoZW4JwAAABAsFL3WXr+tDK5tjR/0ZBVuNyzyqSa8Li2tUMUmB23PWuPG71ObUPTShkhlc7ydNN/qYRaA/Mafm+vsIQWDbCRDA==" - - xdrEnv, err := unmarshalBase64(txeB64) - if assert.NoError(t, err) { - var ca CreateAccount - err = ca.FromXDR(xdrEnv.Operations()[0]) - if assert.NoError(t, err) { - assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", ca.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, "GCPHE7DYMAUAY4UWA6OIYDFGLKRXGLLEMT6MVETC36L7LW4Z3A37EJW5", ca.Destination, "destination should match") - assert.Equal(t, "10000.0000000", ca.Amount, "starting balance should match") - } - } - - txeB64NoSource := "AAAAAGigiN2q4qBXAERImNEncpaADylyBRtzdqpEsku6CN0xAAAAyAAADXYAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAAAAAdgRnAAAAAAAAAAAA" - xdrEnv, err = unmarshalBase64(txeB64NoSource) - if assert.NoError(t, err) { - var ca CreateAccount - err = ca.FromXDR(xdrEnv.Operations()[0]) - if assert.NoError(t, err) { - assert.Equal(t, nil, ca.SourceAccount, "source accounts should match") - assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", ca.Destination, "destination should match") - assert.Equal(t, "198.0000000", ca.Amount, "starting balance should match") - } - } - -} - -func TestPaymentFromXDR(t *testing.T) { - txeB64 := "AAAAAGigiN2q4qBXAERImNEncpaADylyBRtzdqpEsku6CN0xAAABkAAADXYAAAABAAAAAAAAAAAAAAACAAAAAQAAAABooIjdquKgVwBESJjRJ3KWgA8pcgUbc3aqRLJLugjdMQAAAAEAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAAAAAAAAAAAX14QAAAAAAAAAAAQAAAAAQfdFrLDgzSIIugR73qs8U0ZiKbwBUclTTPh5thlbgnAAAAAFYWQAAAAAAAGigiN2q4qBXAERImNEncpaADylyBRtzdqpEsku6CN0xAAAAAE/exwAAAAAAAAAAAA==" - - xdrEnv, err := unmarshalBase64(txeB64) - if assert.NoError(t, err) { - var p Payment - err = p.FromXDR(xdrEnv.Operations()[0]) - if assert.NoError(t, err) { - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", p.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", p.Destination, "destination should match") - assert.Equal(t, "10.0000000", p.Amount, "amount should match") - assert.Equal(t, true, p.Asset.IsNative(), "Asset should be native") - } - - err = p.FromXDR(xdrEnv.Operations()[1]) - if assert.NoError(t, err) { - assert.Equal(t, nil, p.SourceAccount, "source accounts should match") - assert.Equal(t, "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR", p.Destination, "destination should match") - assert.Equal(t, "134.0000000", p.Amount, "amount should match") - assetType, e := p.Asset.GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum4, assetType, "Asset type should match") - assert.Equal(t, "XY", p.Asset.GetCode(), "Asset code should match") - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", p.Asset.GetIssuer(), "Asset issuer should match") - } - } -} - -func TestPathPaymentFromXDR(t *testing.T) { - txeB64 := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAAql0AAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAF9eEAAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAAmJaAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAEuFVmYAAAAQF2kLUL/RoFIy1cmt+GXdWn2tDUjJYV3YwF4A82zIBhqYSO6ogOoLPNRt3w+IGCAgfR4Q9lpax+wCXWoQERHSw4=" - - xdrEnv, err := unmarshalBase64(txeB64) - if assert.NoError(t, err) { - var pp PathPayment - err = pp.FromXDR(xdrEnv.Operations()[0]) - if assert.NoError(t, err) { - assert.Equal(t, nil, pp.SourceAccount, "source accounts should match") - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", pp.Destination, "destination should match") - assert.Equal(t, "1.0000000", pp.DestAmount, "DestAmount should match") - assert.Equal(t, "10.0000000", pp.SendMax, "SendMax should match") - assert.Equal(t, true, pp.DestAsset.IsNative(), "DestAsset should be native") - assert.Equal(t, 1, len(pp.Path), "Number of paths should be 1") - assetType, e := pp.Path[0].GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum4, assetType, "Asset type should match") - assert.Equal(t, "ABCD", pp.Path[0].GetCode(), "Asset code should match") - assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", pp.Path[0].GetIssuer(), "Asset issuer should match") - } - } -} - -func TestManageSellOfferFromXDR(t *testing.T) { - txeB64 := "AAAAAGigiN2q4qBXAERImNEncpaADylyBRtzdqpEsku6CN0xAAABkAAADXYAAAABAAAAAAAAAAAAAAACAAAAAQAAAABooIjdquKgVwBESJjRJ3KWgA8pcgUbc3aqRLJLugjdMQAAAAMAAAAAAAAAAkFCQ1hZWgAAAAAAAAAAAABooIjdquKgVwBESJjRJ3KWgA8pcgUbc3aqRLJLugjdMQAAAACy0F4AAAAABQAAAAEAAAAAAAAAAAAAAAAAAAADAAAAAUFCQwAAAAAAaKCI3arioFcAREiY0SdyloAPKXIFG3N2qkSyS7oI3TEAAAAAAAAAAO5rKAAAAAAFAAAAAQAAAAAAAAAAAAAAAAAAAAA=" - - xdrEnv, err := unmarshalBase64(txeB64) - if assert.NoError(t, err) { - var mso ManageSellOffer - err = mso.FromXDR(xdrEnv.Operations()[0]) - if assert.NoError(t, err) { - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mso.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, int64(0), mso.OfferID, "OfferID should match") - assert.Equal(t, "300.0000000", mso.Amount, "Amount should match") - assert.Equal(t, "5", mso.Price, "Price should match") - assert.Equal(t, true, mso.Selling.IsNative(), "Selling should be native") - assetType, e := mso.Buying.GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum12, assetType, "Asset type should match") - assert.Equal(t, "ABCXYZ", mso.Buying.GetCode(), "Asset code should match") - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mso.Buying.GetIssuer(), "Asset issuer should match") - } - - err = mso.FromXDR(xdrEnv.Operations()[1]) - if assert.NoError(t, err) { - assert.Equal(t, nil, mso.SourceAccount, "source accounts should match") - assert.Equal(t, int64(0), mso.OfferID, "OfferID should match") - assert.Equal(t, "400.0000000", mso.Amount, "Amount should match") - assert.Equal(t, "5", mso.Price, "Price should match") - assert.Equal(t, true, mso.Buying.IsNative(), "Buying should be native") - assetType, e := mso.Selling.GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum4, assetType, "Asset type should match") - assert.Equal(t, "ABC", mso.Selling.GetCode(), "Asset code should match") - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mso.Selling.GetIssuer(), "Asset issuer should match") - } - - } -} - -func TestManageBuyOfferFromXDR(t *testing.T) { - txeB64 := "AAAAAGigiN2q4qBXAERImNEncpaADylyBRtzdqpEsku6CN0xAAABkAAADXYAAAABAAAAAAAAAAAAAAACAAAAAQAAAABooIjdquKgVwBESJjRJ3KWgA8pcgUbc3aqRLJLugjdMQAAAAwAAAAAAAAAAkFCQ1hZWgAAAAAAAAAAAABooIjdquKgVwBESJjRJ3KWgA8pcgUbc3aqRLJLugjdMQAAAAA7msoAAAAAAQAAAAIAAAAAAAAAAAAAAAAAAAAMAAAAAUFCQwAAAAAAaKCI3arioFcAREiY0SdyloAPKXIFG3N2qkSyS7oI3TEAAAAAAAAAALLQXgAAAAADAAAABQAAAAAAAAAAAAAAAAAAAAA=" - - xdrEnv, err := unmarshalBase64(txeB64) - if assert.NoError(t, err) { - var mbo ManageBuyOffer - err = mbo.FromXDR(xdrEnv.Operations()[0]) - if assert.NoError(t, err) { - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mbo.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, int64(0), mbo.OfferID, "OfferID should match") - assert.Equal(t, "100.0000000", mbo.Amount, "Amount should match") - assert.Equal(t, "0.5", mbo.Price, "Price should match") - assert.Equal(t, true, mbo.Selling.IsNative(), "Selling should be native") - assetType, e := mbo.Buying.GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum12, assetType, "Asset type should match") - assert.Equal(t, "ABCXYZ", mbo.Buying.GetCode(), "Asset code should match") - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mbo.Buying.GetIssuer(), "Asset issuer should match") - } - - err = mbo.FromXDR(xdrEnv.Operations()[1]) - if assert.NoError(t, err) { - assert.Equal(t, nil, mbo.SourceAccount, "source accounts should match") - assert.Equal(t, int64(0), mbo.OfferID, "OfferID should match") - assert.Equal(t, "300.0000000", mbo.Amount, "Amount should match") - assert.Equal(t, "0.6", mbo.Price, "Price should match") - assert.Equal(t, true, mbo.Buying.IsNative(), "Buying should be native") - assetType, e := mbo.Selling.GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum4, assetType, "Asset type should match") - assert.Equal(t, "ABC", mbo.Selling.GetCode(), "Asset code should match") - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", mbo.Selling.GetIssuer(), "Asset issuer should match") - } - - } -} - -func TestCreatePassiveSellOfferFromXDR(t *testing.T) { - txeB64 := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAANAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAX14QAAAAABAAAAAQAAAAAAAAAB0odkfgAAAEAgUD7M1UL7x2m2m26ySzcSHxIneOT7/r+s/HLsgWDj6CmpSi1GZrlvtBH+CNuegCwvW09TRZJhp7bLywkaFCoK" - - xdrEnv, err := unmarshalBase64(txeB64) - if assert.NoError(t, err) { - var cpo CreatePassiveSellOffer - err = cpo.FromXDR(xdrEnv.Operations()[0]) - if assert.NoError(t, err) { - assert.Equal(t, nil, cpo.SourceAccount, "source accounts should match") - assert.Equal(t, "10.0000000", cpo.Amount, "Amount should match") - assert.Equal(t, "1", cpo.Price, "Price should match") - assert.Equal(t, true, cpo.Selling.IsNative(), "Selling should be native") - assetType, e := cpo.Buying.GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum4, assetType, "Asset type should match") - assert.Equal(t, "ABCD", cpo.Buying.GetCode(), "Asset code should match") - assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", cpo.Buying.GetIssuer(), "Asset issuer should match") - } - } -} - -func TestSetOptionsFromXDR(t *testing.T) { - - var opSource xdr.AccountId - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") - assert.NoError(t, err) - cFlags := xdr.Uint32(5) - sFlags := xdr.Uint32(7) - mw := xdr.Uint32(7) - lt := xdr.Uint32(2) - mt := xdr.Uint32(4) - ht := xdr.Uint32(6) - hDomain := xdr.String32("stellar.org") - var skey xdr.SignerKey - err = skey.SetAddress("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") - assert.NoError(t, err) - signer := xdr.Signer{ - Key: skey, - Weight: xdr.Uint32(4), - } - - xdrSetOptions := xdr.SetOptionsOp{ - InflationDest: &opSource, - ClearFlags: &cFlags, - SetFlags: &sFlags, - MasterWeight: &mw, - LowThreshold: <, - MedThreshold: &mt, - HighThreshold: &ht, - HomeDomain: &hDomain, - Signer: &signer, - } - - muxSource := opSource.ToMuxedAccount() - xdrOp := xdr.Operation{ - SourceAccount: &muxSource, - Body: xdr.OperationBody{ - Type: xdr.OperationTypeSetOptions, - SetOptionsOp: &xdrSetOptions, - }, - } - - var so SetOptions - err = so.FromXDR(xdrOp) - if assert.NoError(t, err) { - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", so.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, Threshold(7), *so.MasterWeight, "master weight should match") - assert.Equal(t, Threshold(2), *so.LowThreshold, "low threshold should match") - assert.Equal(t, Threshold(4), *so.MediumThreshold, "medium threshold should match") - assert.Equal(t, Threshold(6), *so.HighThreshold, "high threshold should match") - assert.Equal(t, "stellar.org", *so.HomeDomain, "Home domain should match") - assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", so.Signer.Address, "Signer address should match") - assert.Equal(t, Threshold(4), so.Signer.Weight, "Signer weight should match") - assert.Equal(t, int(AuthRequired), int(so.SetFlags[0]), "Set AuthRequired flags should match") - assert.Equal(t, int(AuthRevocable), int(so.SetFlags[1]), "Set AuthRevocable flags should match") - assert.Equal(t, int(AuthImmutable), int(so.SetFlags[2]), "Set AuthImmutable flags should match") - assert.Equal(t, int(AuthRequired), int(so.ClearFlags[0]), "Clear AuthRequired flags should match") - assert.Equal(t, int(AuthImmutable), int(so.ClearFlags[1]), "Clear AuthImmutable flags should match") - } - -} - -func TestChangeTrustFromXDR(t *testing.T) { - asset := CreditAsset{Code: "ABC", Issuer: "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3"} - xdrAsset, err := asset.ToXDR() - assert.NoError(t, err) - xdrLimit, err := amount.Parse("5000") - assert.NoError(t, err) - changeTrustOp := xdr.ChangeTrustOp{ - Line: xdrAsset, - Limit: xdrLimit, - } - - var opSource xdr.MuxedAccount - err = opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") - assert.NoError(t, err) - xdrOp := xdr.Operation{ - SourceAccount: &opSource, - Body: xdr.OperationBody{ - Type: xdr.OperationTypeChangeTrust, - ChangeTrustOp: &changeTrustOp, - }, - } - - var ct ChangeTrust - err = ct.FromXDR(xdrOp) - if assert.NoError(t, err) { - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", ct.SourceAccount.GetAccountID(), "source accounts should match") - assetType, e := ct.Line.GetType() - assert.NoError(t, e) - - assert.Equal(t, AssetTypeCreditAlphanum4, assetType, "Asset type should match") - assert.Equal(t, "ABC", ct.Line.GetCode(), "Asset code should match") - assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", ct.Line.GetIssuer(), "Asset issuer should match") - assert.Equal(t, "5000.0000000", ct.Limit, "Trustline limit should match") - } -} - -func TestAllowTrustFromXDR(t *testing.T) { - xdrAsset := xdr.Asset{} - allowTrustAsset, err := xdrAsset.ToAllowTrustOpAsset("ABCXYZ") - assert.NoError(t, err) - - var opSource xdr.MuxedAccount - err = opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") - assert.NoError(t, err) - - var trustor xdr.AccountId - err = trustor.SetAddress("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") - assert.NoError(t, err) - - allowTrustOp := xdr.AllowTrustOp{ - Trustor: trustor, - Asset: allowTrustAsset, - Authorize: xdr.Uint32(xdr.TrustLineFlagsAuthorizedFlag), - } - - xdrOp := xdr.Operation{ - SourceAccount: &opSource, - Body: xdr.OperationBody{ - Type: xdr.OperationTypeAllowTrust, - AllowTrustOp: &allowTrustOp, - }, - } - - var at AllowTrust - err = at.FromXDR(xdrOp) - if assert.NoError(t, err) { - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", at.SourceAccount.GetAccountID(), "source accounts should match") - - assetType, e := at.Type.GetType() - assert.NoError(t, e) - assert.Equal(t, AssetTypeCreditAlphanum12, assetType, "Asset type should match") - assert.Equal(t, "ABCXYZ", at.Type.GetCode(), "Asset code should match") - assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", at.Trustor, "Trustor should match") - assert.Equal(t, true, at.Authorize, "Authorize value should match") - } -} - -func TestAccountMergeFromXDR(t *testing.T) { - var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") - assert.NoError(t, err) - - var destination xdr.MuxedAccount - err = destination.SetAddress("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") - assert.NoError(t, err) - - xdrOp := xdr.Operation{ - SourceAccount: &opSource, - Body: xdr.OperationBody{ - Type: xdr.OperationTypeAccountMerge, - Destination: &destination, - }, - } - - var am AccountMerge - err = am.FromXDR(xdrOp) - if assert.NoError(t, err) { - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", am.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", am.Destination, "destination accounts should match") - } -} - -func TestInflationFromXDR(t *testing.T) { - var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") - assert.NoError(t, err) - - xdrOp := xdr.Operation{ - SourceAccount: &opSource, - Body: xdr.OperationBody{Type: xdr.OperationTypeInflation}, - } - - var inf Inflation - err = inf.FromXDR(xdrOp) - if assert.NoError(t, err) { - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", inf.SourceAccount.GetAccountID(), "source accounts should match") - } -} - -func TestManageDataFromXDR(t *testing.T) { - var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") - assert.NoError(t, err) - - dv := []byte("value") - xdrdv := xdr.DataValue(dv) - manageDataOp := xdr.ManageDataOp{ - DataName: xdr.String64("data"), - DataValue: &xdrdv, - } - - xdrOp := xdr.Operation{ - SourceAccount: &opSource, - Body: xdr.OperationBody{ - Type: xdr.OperationTypeManageData, - ManageDataOp: &manageDataOp, - }, - } - - var md ManageData - err = md.FromXDR(xdrOp) - if assert.NoError(t, err) { - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", md.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, "data", md.Name, "Name should match") - assert.Equal(t, "value", string(md.Value), "Value should match") - } -} - -func TestBumpSequenceFromXDR(t *testing.T) { - var opSource xdr.MuxedAccount - err := opSource.SetAddress("GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H") - assert.NoError(t, err) - - bsOp := xdr.BumpSequenceOp{ - BumpTo: xdr.SequenceNumber(45), - } - - xdrOp := xdr.Operation{ - SourceAccount: &opSource, - Body: xdr.OperationBody{ - Type: xdr.OperationTypeBumpSequence, - BumpSequenceOp: &bsOp, - }, - } - - var bs BumpSequence - err = bs.FromXDR(xdrOp) - if assert.NoError(t, err) { - assert.Equal(t, "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", bs.SourceAccount.GetAccountID(), "source accounts should match") - assert.Equal(t, int64(45), bs.BumpTo, "BumpTo should match") - } -} diff --git a/services/horizon/internal/txnbuild/path_payment.go b/services/horizon/internal/txnbuild/path_payment.go deleted file mode 100644 index eb4fc7bccc..0000000000 --- a/services/horizon/internal/txnbuild/path_payment.go +++ /dev/null @@ -1,167 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// PathPayment represents the Stellar path_payment operation. This operation was removed -// in Stellar Protocol 12 and replaced by PathPaymentStrictReceive. -// Deprecated: This operation was renamed to PathPaymentStrictReceive, -// which functions identically. -type PathPayment = PathPaymentStrictReceive - -// PathPaymentStrictReceive represents the Stellar path_payment_strict_receive operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type PathPaymentStrictReceive struct { - SendAsset Asset - SendMax string - Destination string - DestAsset Asset - DestAmount string - Path []Asset - SourceAccount Account -} - -// BuildXDR for PathPaymentStrictReceive returns a fully configured XDR Operation. -func (pp *PathPaymentStrictReceive) BuildXDR() (xdr.Operation, error) { - // Set XDR send asset - if pp.SendAsset == nil { - return xdr.Operation{}, errors.New("you must specify an asset to send for payment") - } - xdrSendAsset, err := pp.SendAsset.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set asset type") - } - - // Set XDR send max - xdrSendMax, err := amount.Parse(pp.SendMax) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse maximum amount to send") - } - - // Set XDR destination - var xdrDestination xdr.MuxedAccount - err = xdrDestination.SetAddress(pp.Destination) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") - } - - // Set XDR destination asset - if pp.DestAsset == nil { - return xdr.Operation{}, errors.New("you must specify an asset for destination account to receive") - } - xdrDestAsset, err := pp.DestAsset.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set asset type") - } - - // Set XDR destination amount - xdrDestAmount, err := amount.Parse(pp.DestAmount) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse amount of asset destination account receives") - } - - // Set XDR path - var xdrPath []xdr.Asset - var xdrPathAsset xdr.Asset - for _, asset := range pp.Path { - xdrPathAsset, err = asset.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set asset type") - } - xdrPath = append(xdrPath, xdrPathAsset) - } - - opType := xdr.OperationTypePathPaymentStrictReceive - xdrOp := xdr.PathPaymentStrictReceiveOp{ - SendAsset: xdrSendAsset, - SendMax: xdrSendMax, - Destination: xdrDestination, - DestAsset: xdrDestAsset, - DestAmount: xdrDestAmount, - Path: xdrPath, - } - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, pp.SourceAccount) - return op, nil -} - -// FromXDR for PathPaymentStrictReceive initialises the txnbuild struct from the corresponding xdr Operation. -func (pp *PathPaymentStrictReceive) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetPathPaymentStrictReceiveOp() - if !ok { - return errors.New("error parsing path_payment operation from xdr") - } - - pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - destAID := result.Destination.ToAccountId() - pp.Destination = destAID.Address() - pp.DestAmount = amount.String(result.DestAmount) - pp.SendMax = amount.String(result.SendMax) - - destAsset, err := assetFromXDR(result.DestAsset) - if err != nil { - return errors.Wrap(err, "error parsing dest_asset in path_payment operation") - } - pp.DestAsset = destAsset - - sendAsset, err := assetFromXDR(result.SendAsset) - if err != nil { - return errors.Wrap(err, "error parsing send_asset in path_payment operation") - } - pp.SendAsset = sendAsset - - pp.Path = []Asset{} - for _, p := range result.Path { - pathAsset, err := assetFromXDR(p) - if err != nil { - return errors.Wrap(err, "error parsing paths in path_payment operation") - } - pp.Path = append(pp.Path, pathAsset) - } - - return nil -} - -// Validate for PathPaymentStrictReceive validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (pp *PathPaymentStrictReceive) Validate() error { - _, err := xdr.AddressToAccountId(pp.Destination) - if err != nil { - return NewValidationError("Destination", err.Error()) - } - - err = validateStellarAsset(pp.SendAsset) - if err != nil { - return NewValidationError("SendAsset", err.Error()) - } - - err = validateStellarAsset(pp.DestAsset) - if err != nil { - return NewValidationError("DestAsset", err.Error()) - } - - err = validateAmount(pp.SendMax) - if err != nil { - return NewValidationError("SendMax", err.Error()) - } - - err = validateAmount(pp.DestAmount) - if err != nil { - return NewValidationError("DestAmount", err.Error()) - } - - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (pp *PathPaymentStrictReceive) GetSourceAccount() Account { - return pp.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/path_payment_strict_send.go b/services/horizon/internal/txnbuild/path_payment_strict_send.go deleted file mode 100644 index 94fb553aa2..0000000000 --- a/services/horizon/internal/txnbuild/path_payment_strict_send.go +++ /dev/null @@ -1,161 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// PathPaymentStrictSend represents the Stellar path_payment_strict_send operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type PathPaymentStrictSend struct { - SendAsset Asset - SendAmount string - Destination string - DestAsset Asset - DestMin string - Path []Asset - SourceAccount Account -} - -// BuildXDR for Payment returns a fully configured XDR Operation. -func (pp *PathPaymentStrictSend) BuildXDR() (xdr.Operation, error) { - // Set XDR send asset - if pp.SendAsset == nil { - return xdr.Operation{}, errors.New("you must specify an asset to send for payment") - } - xdrSendAsset, err := pp.SendAsset.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set asset type") - } - - // Set XDR dest min - xdrDestMin, err := amount.Parse(pp.DestMin) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse minimum amount to receive") - } - - // Set XDR destination - var xdrDestination xdr.MuxedAccount - err = xdrDestination.SetAddress(pp.Destination) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") - } - - // Set XDR destination asset - if pp.DestAsset == nil { - return xdr.Operation{}, errors.New("you must specify an asset for destination account to receive") - } - xdrDestAsset, err := pp.DestAsset.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set asset type") - } - - // Set XDR destination amount - xdrSendAmount, err := amount.Parse(pp.SendAmount) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse amount of asset source account sends") - } - - // Set XDR path - var xdrPath []xdr.Asset - var xdrPathAsset xdr.Asset - for _, asset := range pp.Path { - xdrPathAsset, err = asset.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set asset type") - } - xdrPath = append(xdrPath, xdrPathAsset) - } - - opType := xdr.OperationTypePathPaymentStrictSend - xdrOp := xdr.PathPaymentStrictSendOp{ - SendAsset: xdrSendAsset, - SendAmount: xdrSendAmount, - Destination: xdrDestination, - DestAsset: xdrDestAsset, - DestMin: xdrDestMin, - Path: xdrPath, - } - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, pp.SourceAccount) - return op, nil -} - -// FromXDR for PathPaymentStrictSend initialises the txnbuild struct from the corresponding xdr Operation. -func (pp *PathPaymentStrictSend) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetPathPaymentStrictSendOp() - if !ok { - return errors.New("error parsing path_payment operation from xdr") - } - - pp.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - destAID := result.Destination.ToAccountId() - pp.Destination = destAID.Address() - pp.SendAmount = amount.String(result.SendAmount) - pp.DestMin = amount.String(result.DestMin) - - destAsset, err := assetFromXDR(result.DestAsset) - if err != nil { - return errors.Wrap(err, "error parsing dest_asset in path_payment operation") - } - pp.DestAsset = destAsset - - sendAsset, err := assetFromXDR(result.SendAsset) - if err != nil { - return errors.Wrap(err, "error parsing send_asset in path_payment operation") - } - pp.SendAsset = sendAsset - - pp.Path = []Asset{} - for _, p := range result.Path { - pathAsset, err := assetFromXDR(p) - if err != nil { - return errors.Wrap(err, "error parsing paths in path_payment operation") - } - pp.Path = append(pp.Path, pathAsset) - } - - return nil -} - -// Validate for PathPaymentStrictSend validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (pp *PathPaymentStrictSend) Validate() error { - _, err := xdr.AddressToAccountId(pp.Destination) - if err != nil { - return NewValidationError("Destination", err.Error()) - } - - err = validateStellarAsset(pp.SendAsset) - if err != nil { - return NewValidationError("SendAsset", err.Error()) - } - - err = validateStellarAsset(pp.DestAsset) - if err != nil { - return NewValidationError("DestAsset", err.Error()) - } - - err = validateAmount(pp.SendAmount) - if err != nil { - return NewValidationError("SendAmount", err.Error()) - } - - err = validateAmount(pp.DestMin) - if err != nil { - return NewValidationError("DestMin", err.Error()) - } - - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (pp *PathPaymentStrictSend) GetSourceAccount() Account { - return pp.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/path_payment_strict_send_test.go b/services/horizon/internal/txnbuild/path_payment_strict_send_test.go deleted file mode 100644 index 66a79a7b58..0000000000 --- a/services/horizon/internal/txnbuild/path_payment_strict_send_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPathPaymentStrictSendValidateSendAsset(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPaymentStrictSend{ - SendAsset: CreditAsset{"ABCD", ""}, - SendAmount: "10", - Destination: kp2.Address(), - DestAsset: NativeAsset{}, - DestMin: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictSend operation: Field: SendAsset, Error: asset issuer: public key is undefined" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentStrictSendValidateDestAsset(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPaymentStrictSend{ - SendAsset: NativeAsset{}, - SendAmount: "10", - Destination: kp2.Address(), - DestAsset: CreditAsset{"", kp0.Address()}, - DestMin: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictSend operation: Field: DestAsset, Error: asset code length must be between 1 and 12 characters" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentStrictSendValidateDestination(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPaymentStrictSend{ - SendAsset: NativeAsset{}, - SendAmount: "10", - Destination: "SASND3NRUY5K43PN3H3HOP5JNTIDXJFLOKKNSCZQQAFBRSEIRD5OJKXZ", - DestAsset: CreditAsset{"ABCD", kp0.Address()}, - DestMin: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictSend operation: Field: Destination" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentStrictSendValidateSendMax(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPaymentStrictSend{ - SendAsset: NativeAsset{}, - SendAmount: "abc", - Destination: kp2.Address(), - DestAsset: CreditAsset{"ABCD", kp0.Address()}, - DestMin: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictSend operation: Field: SendAmount, Error: invalid amount format: abc" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentStrictSendValidateDestAmount(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPaymentStrictSend{ - SendAsset: NativeAsset{}, - SendAmount: "10", - Destination: kp2.Address(), - DestAsset: CreditAsset{"ABCD", kp0.Address()}, - DestMin: "-1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictSend operation: Field: DestMin, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/path_payment_test.go b/services/horizon/internal/txnbuild/path_payment_test.go deleted file mode 100644 index e8af782046..0000000000 --- a/services/horizon/internal/txnbuild/path_payment_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPathPaymentValidateSendAsset(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPayment{ - SendAsset: CreditAsset{"ABCD", ""}, - SendMax: "10", - Destination: kp2.Address(), - DestAsset: NativeAsset{}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictReceive operation: Field: SendAsset, Error: asset issuer: public key is undefined" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentValidateDestAsset(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPayment{ - SendAsset: NativeAsset{}, - SendMax: "10", - Destination: kp2.Address(), - DestAsset: CreditAsset{"", kp0.Address()}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictReceive operation: Field: DestAsset, Error: asset code length must be between 1 and 12 characters" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentValidateDestination(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPayment{ - SendAsset: NativeAsset{}, - SendMax: "10", - Destination: "SASND3NRUY5K43PN3H3HOP5JNTIDXJFLOKKNSCZQQAFBRSEIRD5OJKXZ", - DestAsset: CreditAsset{"ABCD", kp0.Address()}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictReceive operation: Field: Destination" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentValidateSendMax(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPayment{ - SendAsset: NativeAsset{}, - SendMax: "abc", - Destination: kp2.Address(), - DestAsset: CreditAsset{"ABCD", kp0.Address()}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictReceive operation: Field: SendMax, Error: invalid amount format: abc" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPathPaymentValidateDestAmount(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPayment{ - SendAsset: NativeAsset{}, - SendMax: "10", - Destination: kp2.Address(), - DestAsset: CreditAsset{"ABCD", kp0.Address()}, - DestAmount: "-1", - Path: []Asset{abcdAsset}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.PathPaymentStrictReceive operation: Field: DestAmount, Error: amount can not be negative" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/payment.go b/services/horizon/internal/txnbuild/payment.go deleted file mode 100644 index ed5180f0d7..0000000000 --- a/services/horizon/internal/txnbuild/payment.go +++ /dev/null @@ -1,101 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/amount" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// Payment represents the Stellar payment operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type Payment struct { - Destination string - Amount string - Asset Asset - SourceAccount Account -} - -// BuildXDR for Payment returns a fully configured XDR Operation. -func (p *Payment) BuildXDR() (xdr.Operation, error) { - var destMuxedAccount xdr.MuxedAccount - - err := destMuxedAccount.SetAddress(p.Destination) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set destination address") - } - - xdrAmount, err := amount.Parse(p.Amount) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to parse amount") - } - - if p.Asset == nil { - return xdr.Operation{}, errors.New("you must specify an asset for payment") - } - xdrAsset, err := p.Asset.ToXDR() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set asset type") - } - - opType := xdr.OperationTypePayment - xdrOp := xdr.PaymentOp{ - Destination: destMuxedAccount, - Amount: xdrAmount, - Asset: xdrAsset, - } - body, err := xdr.NewOperationBody(opType, xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR Operation") - } - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, p.SourceAccount) - return op, nil -} - -// FromXDR for Payment initialises the txnbuild struct from the corresponding xdr Operation. -func (p *Payment) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetPaymentOp() - if !ok { - return errors.New("error parsing payment operation from xdr") - } - - p.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - destAID := result.Destination.ToAccountId() - p.Destination = destAID.Address() - p.Amount = amount.String(result.Amount) - - asset, err := assetFromXDR(result.Asset) - if err != nil { - return errors.Wrap(err, "error parsing asset in payment operation") - } - p.Asset = asset - - return nil -} - -// Validate for Payment validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (p *Payment) Validate() error { - _, err := xdr.AddressToAccountId(p.Destination) - if err != nil { - return NewValidationError("Destination", err.Error()) - } - - err = validateStellarAsset(p.Asset) - if err != nil { - return NewValidationError("Asset", err.Error()) - } - - err = validateAmount(p.Amount) - if err != nil { - return NewValidationError("Amount", err.Error()) - } - - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (p *Payment) GetSourceAccount() Account { - return p.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/payment_test.go b/services/horizon/internal/txnbuild/payment_test.go deleted file mode 100644 index f8e94daa92..0000000000 --- a/services/horizon/internal/txnbuild/payment_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPaymentValidateDestination(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - payment := Payment{ - Destination: "", - Amount: "10", - Asset: NativeAsset{}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&payment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.Payment operation: Field: Destination" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPaymentValidateAmount(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - payment := Payment{ - Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", - Amount: "ten", - Asset: NativeAsset{}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&payment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.Payment operation: Field: Amount, Error: invalid amount format: ten" - assert.Contains(t, err.Error(), expected) - } -} - -func TestPaymentValidateAsset(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - payment := Payment{ - Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", - Amount: "10", - Asset: CreditAsset{}, - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&payment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - if assert.Error(t, err) { - expected := "validation failed for *txnbuild.Payment operation: Field: Asset, Error: asset code length must be between 1 and 12 characters" - assert.Contains(t, err.Error(), expected) - } -} diff --git a/services/horizon/internal/txnbuild/price.go b/services/horizon/internal/txnbuild/price.go deleted file mode 100644 index 9433379df1..0000000000 --- a/services/horizon/internal/txnbuild/price.go +++ /dev/null @@ -1,63 +0,0 @@ -package txnbuild - -import ( - "strconv" - - pricepkg "github.com/stellar/go/price" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -type price struct { - n int - d int - s string -} - -func (p *price) parse(s string) error { - if len(s) == 0 { - return errors.New("cannot parse price from empty string") - } - - xdrPrice, err := pricepkg.Parse(s) - if err != nil { - return errors.Wrap(err, "failed to parse price from string") - } - - if len(p.s) > 0 { - inverse, err := pricepkg.Parse(p.s) - if err == nil && xdrPrice == inverse { - return nil - } - } - - p.n = int(xdrPrice.N) - p.d = int(xdrPrice.D) - p.s = s - return nil -} - -func (p *price) fromXDR(xdrPrice xdr.Price) { - n := int(xdrPrice.N) - d := int(xdrPrice.D) - if n == p.n && d == p.d { - return - } - p.n = n - p.d = d - v := float64(n) / float64(d) - // The special precision -1 uses the smallest number of digits - // necessary such that ParseFloat will return f exactly. - p.s = strconv.FormatFloat(v, 'f', -1, 32) -} - -func (p price) string() string { - return p.s -} - -func (p price) toXDR() xdr.Price { - return xdr.Price{ - N: xdr.Int32(p.n), - D: xdr.Int32(p.d), - } -} diff --git a/services/horizon/internal/txnbuild/price_test.go b/services/horizon/internal/txnbuild/price_test.go deleted file mode 100644 index 70b94453ec..0000000000 --- a/services/horizon/internal/txnbuild/price_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package txnbuild - -import ( - "math" - "testing" - - "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" -) - -func TestPriceFromXDR(t *testing.T) { - for _, testCase := range []struct { - name string - input xdr.Price - expected price - }{ - { - "1/2", - xdr.Price{N: 1, D: 2}, - price{n: 1, d: 2, s: "0.5"}, - }, - { - "1", - xdr.Price{N: 1, D: 1}, - price{n: 1, d: 1, s: "1"}, - }, - { - "1 / 1000000000", - xdr.Price{N: 1, D: 1000000000}, - price{n: 1, d: 1000000000, s: "0.000000001"}, - }, - { - "max int 32", - xdr.Price{N: math.MaxInt32, D: 1}, - price{n: math.MaxInt32, d: 1, s: "2147483600"}, - }, - { - "1/3", - xdr.Price{N: 1, D: 3}, - price{n: 1, d: 3, s: "0.33333334"}, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - var p price - p.fromXDR(testCase.input) - - assert.Equal(t, testCase.expected, p) - assert.Equal(t, testCase.input, p.toXDR()) - - assert.NoError(t, p.parse(p.string())) - assert.Equal(t, testCase.expected, p) - }) - } -} - -func TestPriceParse(t *testing.T) { - var p price - assert.NoError(t, p.parse("0.5")) - assert.Equal(t, price{n: 1, d: 2, s: "0.5"}, p) - - assert.NoError(t, p.parse("00.5")) - assert.Equal(t, price{n: 1, d: 2, s: "0.5"}, p) - - assert.NoError(t, p.parse("0.50")) - assert.Equal(t, price{n: 1, d: 2, s: "0.5"}, p) - - assert.EqualError(t, p.parse(""), "cannot parse price from empty string") - assert.Equal(t, price{n: 1, d: 2, s: "0.5"}, p) - - assert.EqualError(t, p.parse("abc"), "failed to parse price from string: invalid price format: abc") - assert.Equal(t, price{n: 1, d: 2, s: "0.5"}, p) - - assert.NoError(t, p.parse("0.33333334")) - assert.Equal(t, price{n: 16666667, d: 50000000, s: "0.33333334"}, p) - - p.fromXDR(xdr.Price{N: 1, D: 3}) - assert.Equal(t, price{n: 1, d: 3, s: "0.33333334"}, p) - assert.NoError(t, p.parse("00.33333334")) - assert.Equal(t, price{n: 1, d: 3, s: "0.33333334"}, p) - assert.NoError(t, p.parse("0.333333340")) - assert.Equal(t, price{n: 1, d: 3, s: "0.33333334"}, p) -} diff --git a/services/horizon/internal/txnbuild/set_options.go b/services/horizon/internal/txnbuild/set_options.go deleted file mode 100644 index 1ec99970fe..0000000000 --- a/services/horizon/internal/txnbuild/set_options.go +++ /dev/null @@ -1,325 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// AccountFlag represents the bitmask flags used to set and clear account authorization options. -type AccountFlag uint32 - -// AuthRequired is a flag that requires the issuing account to give other accounts -// permission before they can hold the issuing account's credit. -const AuthRequired = AccountFlag(xdr.AccountFlagsAuthRequiredFlag) - -// AuthRevocable is a flag that allows the issuing account to revoke its credit -// held by other accounts. -const AuthRevocable = AccountFlag(xdr.AccountFlagsAuthRevocableFlag) - -// AuthImmutable is a flag that if set prevents any authorization flags from being -// set, and prevents the account from ever being merged (deleted). -const AuthImmutable = AccountFlag(xdr.AccountFlagsAuthImmutableFlag) - -// Threshold is the datatype for MasterWeight, Signer.Weight, and Thresholds. Each is a number -// between 0-255 inclusive. -type Threshold uint8 - -// Signer represents the Signer in a SetOptions operation. -// If the signer already exists, it is updated. -// If the weight is 0, the signer is deleted. -type Signer struct { - Address string - Weight Threshold -} - -// NewHomeDomain is syntactic sugar that makes instantiating SetOptions more convenient. -func NewHomeDomain(hd string) *string { - return &hd -} - -// NewThreshold is syntactic sugar that makes instantiating SetOptions more convenient. -func NewThreshold(t Threshold) *Threshold { - return &t -} - -// NewInflationDestination is syntactic sugar that makes instantiating SetOptions more convenient. -func NewInflationDestination(ai string) *string { - return &ai -} - -// SetOptions represents the Stellar set options operation. See -// https://www.stellar.org/developers/guides/concepts/list-of-operations.html -type SetOptions struct { - InflationDestination *string - SetFlags []AccountFlag - ClearFlags []AccountFlag - MasterWeight *Threshold - LowThreshold *Threshold - MediumThreshold *Threshold - HighThreshold *Threshold - HomeDomain *string - Signer *Signer - xdrOp xdr.SetOptionsOp - SourceAccount Account -} - -// BuildXDR for SetOptions returns a fully configured XDR Operation. -func (so *SetOptions) BuildXDR() (xdr.Operation, error) { - err := so.handleInflation() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set inflation destination address") - } - - so.handleClearFlags() - so.handleSetFlags() - so.handleMasterWeight() - so.handleLowThreshold() - so.handleMediumThreshold() - so.handleHighThreshold() - err = so.handleHomeDomain() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set home domain") - } - err = so.handleSigner() - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to set signer") - } - - opType := xdr.OperationTypeSetOptions - body, err := xdr.NewOperationBody(opType, so.xdrOp) - if err != nil { - return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody") - } - - op := xdr.Operation{Body: body} - SetOpSourceAccount(&op, so.SourceAccount) - return op, nil -} - -// handleInflation for SetOptions sets the XDR inflation destination. -// Once set, a new address can be set, but there's no way to ever unset. -func (so *SetOptions) handleInflation() (err error) { - if so.InflationDestination != nil { - var xdrAccountID xdr.AccountId - err = xdrAccountID.SetAddress(*so.InflationDestination) - if err != nil { - return - } - so.xdrOp.InflationDest = &xdrAccountID - } - return -} - -// handleInflationXDR for SetOptions sets the inflation destination from a XDR object. -func (so *SetOptions) handleInflationXDR(account *xdr.AccountId) { - if account != nil { - address := account.Address() - so.InflationDestination = &address - } -} - -// handleSetFlags for SetOptions sets XDR account flags (represented as a bitmask). -// See https://www.stellar.org/developers/guides/concepts/accounts.html -func (so *SetOptions) handleSetFlags() { - var flags xdr.Uint32 - for _, flag := range so.SetFlags { - flags = flags | xdr.Uint32(flag) - } - if len(so.SetFlags) > 0 { - so.xdrOp.SetFlags = &flags - } -} - -// handleSetFlagsXDR for SetOptions sets account flags from XDR object (represented as a bitmask). -// See https://www.stellar.org/developers/guides/concepts/accounts.html -func (so *SetOptions) handleSetFlagsXDR(flags *xdr.Uint32) { - if flags != nil { - for _, f := range []AccountFlag{AuthRequired, AuthRevocable, AuthImmutable} { - if f&AccountFlag(*flags) != 0 { - so.SetFlags = append(so.SetFlags, f) - } - } - } -} - -// handleClearFlags for SetOptions unsets XDR account flags (represented as a bitmask). -// See https://www.stellar.org/developers/guides/concepts/accounts.html -func (so *SetOptions) handleClearFlags() { - var flags xdr.Uint32 - for _, flag := range so.ClearFlags { - flags = flags | xdr.Uint32(flag) - } - if len(so.ClearFlags) > 0 { - so.xdrOp.ClearFlags = &flags - } -} - -// handleClearFlagsXDR for SetOptions unsets account flags (represented as a bitmask). -// See https://www.stellar.org/developers/guides/concepts/accounts.html -func (so *SetOptions) handleClearFlagsXDR(flags *xdr.Uint32) { - if flags != nil { - for _, f := range []AccountFlag{AuthRequired, AuthRevocable, AuthImmutable} { - if f&AccountFlag(*flags) != 0 { - so.ClearFlags = append(so.ClearFlags, f) - } - } - } -} - -// handleMasterWeight for SetOptions sets the XDR weight of the master signing key. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleMasterWeight() { - if so.MasterWeight != nil { - xdrWeight := xdr.Uint32(*so.MasterWeight) - so.xdrOp.MasterWeight = &xdrWeight - } -} - -// handleMasterWeightXDR for SetOptions sets the weight of the master signing key. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleMasterWeightXDR(weight *xdr.Uint32) { - if weight != nil { - mw := Threshold(uint32(*weight)) - so.MasterWeight = &mw - } -} - -// handleLowThreshold for SetOptions sets the XDR value of the account's "low" threshold. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleLowThreshold() { - if so.LowThreshold != nil { - xdrThreshold := xdr.Uint32(*so.LowThreshold) - so.xdrOp.LowThreshold = &xdrThreshold - } -} - -// handleLowThresholdXDR for SetOptions sets value of the account's "low" threshold. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleLowThresholdXDR(weight *xdr.Uint32) { - if weight != nil { - lt := Threshold(uint32(*weight)) - so.LowThreshold = < - } -} - -// handleMediumThreshold for SetOptions sets the XDR value of the account's "medium" threshold. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleMediumThreshold() { - if so.MediumThreshold != nil { - xdrThreshold := xdr.Uint32(*so.MediumThreshold) - so.xdrOp.MedThreshold = &xdrThreshold - } -} - -// handleLowMediumXDR for SetOptions sets value of the account's "medium" threshold. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleMediumThresholdXDR(weight *xdr.Uint32) { - if weight != nil { - mt := Threshold(uint32(*weight)) - so.MediumThreshold = &mt - } -} - -// handleHighThreshold for SetOptions sets the XDR value of the account's "high" threshold. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleHighThreshold() { - if so.HighThreshold != nil { - xdrThreshold := xdr.Uint32(*so.HighThreshold) - so.xdrOp.HighThreshold = &xdrThreshold - } -} - -// handleHighThresholdXDR for SetOptions sets value of the account's "high" threshold. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleHighThresholdXDR(weight *xdr.Uint32) { - if weight != nil { - ht := Threshold(uint32(*weight)) - so.HighThreshold = &ht - } -} - -// handleHomeDomain for SetOptions sets the XDR value of the account's home domain. -// https://www.stellar.org/developers/guides/concepts/federation.html -func (so *SetOptions) handleHomeDomain() error { - if so.HomeDomain != nil { - if len(*so.HomeDomain) > 32 { - return errors.New("homeDomain must be 32 characters or less") - } - xdrHomeDomain := xdr.String32(*so.HomeDomain) - so.xdrOp.HomeDomain = &xdrHomeDomain - } - - return nil -} - -// handleHomeDomainXDR for SetOptions sets the value of the account's home domain. -// https://www.stellar.org/developers/guides/concepts/federation.html -func (so *SetOptions) handleHomeDomainXDR(xDomain *xdr.String32) { - if xDomain != nil { - domain := string(*xDomain) - so.HomeDomain = &domain - } -} - -// handleSigner for SetOptions sets the XDR value of a signer for the account. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleSigner() (err error) { - if so.Signer != nil { - var xdrSigner xdr.Signer - xdrWeight := xdr.Uint32(so.Signer.Weight) - xdrSigner.Weight = xdrWeight - err = xdrSigner.Key.SetAddress(so.Signer.Address) - if err != nil { - return - } - - so.xdrOp.Signer = &xdrSigner - } - return nil -} - -// handleSignerXDR for SetOptions sets the value of a signer for the account. -// See https://www.stellar.org/developers/guides/concepts/multi-sig.html -func (so *SetOptions) handleSignerXDR(xSigner *xdr.Signer) { - if xSigner != nil { - newSigner := Signer{} - newSigner.Address = xSigner.Key.Address() - newSigner.Weight = Threshold(uint32(xSigner.Weight)) - so.Signer = &newSigner - } -} - -// FromXDR for SetOptions initialises the txnbuild struct from the corresponding xdr Operation. -func (so *SetOptions) FromXDR(xdrOp xdr.Operation) error { - result, ok := xdrOp.Body.GetSetOptionsOp() - if !ok { - return errors.New("error parsing set_options operation from xdr") - } - - so.SourceAccount = accountFromXDR(xdrOp.SourceAccount) - so.handleInflationXDR(result.InflationDest) - so.handleClearFlagsXDR(result.ClearFlags) - so.handleSetFlagsXDR(result.SetFlags) - so.handleMasterWeightXDR(result.MasterWeight) - so.handleLowThresholdXDR(result.LowThreshold) - so.handleMediumThresholdXDR(result.MedThreshold) - so.handleHighThresholdXDR(result.HighThreshold) - so.handleHomeDomainXDR(result.HomeDomain) - so.handleSignerXDR(result.Signer) - - return nil -} - -// Validate for SetOptions validates the required struct fields. It returns an error if any -// of the fields are invalid. Otherwise, it returns nil. -func (so *SetOptions) Validate() error { - // skipping checks here because the individual methods above already check for required fields. - // Refactoring is out of the scope of this issue(https://github.com/stellar/go/issues/1041) so will leave as is for now. - return nil -} - -// GetSourceAccount returns the source account of the operation, or nil if not -// set. -func (so *SetOptions) GetSourceAccount() Account { - return so.SourceAccount -} diff --git a/services/horizon/internal/txnbuild/set_options_test.go b/services/horizon/internal/txnbuild/set_options_test.go deleted file mode 100644 index 0398f37257..0000000000 --- a/services/horizon/internal/txnbuild/set_options_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" -) - -func TestHandleSetFlagsThreeDifferent(t *testing.T) { - options := SetOptions{} - options.SetFlags = []AccountFlag{1, 2, 4} - - options.handleSetFlags() - - expected := xdr.Uint32(7) - assert.Equal(t, expected, *options.xdrOp.SetFlags, "three different valid flags are ok") -} - -func TestHandleSetFlagsThreeSame(t *testing.T) { - options := SetOptions{} - options.SetFlags = []AccountFlag{1, 1, 1} - - options.handleSetFlags() - - expected := xdr.Uint32(1) - assert.Equal(t, expected, *options.xdrOp.SetFlags, "three of the same valid flags are ok") -} - -func TestHandleSetFlagsRedundantFlagsAllowed(t *testing.T) { - options := SetOptions{} - options.SetFlags = []AccountFlag{1, 2, 4, 2, 4, 1} - - options.handleSetFlags() - - expected := xdr.Uint32(7) - assert.Equal(t, expected, *options.xdrOp.SetFlags, "additional redundant flags are allowed") -} - -func TestHandleSetFlagsLessThanThreeAreOK(t *testing.T) { - options := SetOptions{} - options.SetFlags = []AccountFlag{1, 2} - - options.handleSetFlags() - - expected := xdr.Uint32(3) - assert.Equal(t, expected, *options.xdrOp.SetFlags, "less than three flags are ok") -} - -func TestHandleSetFlagsInvalidFlagsAllowed(t *testing.T) { - options := SetOptions{} - options.SetFlags = []AccountFlag{3, 3, 3} - - options.handleSetFlags() - - expected := xdr.Uint32(3) - assert.Equal(t, expected, *options.xdrOp.SetFlags, "invalid flags are allowed") -} - -func TestHandleSetFlagsZeroFlagsAreOK(t *testing.T) { - options := SetOptions{} - options.SetFlags = []AccountFlag{0, 2, 0} - - options.handleSetFlags() - - expected := xdr.Uint32(2) - assert.Equal(t, expected, *options.xdrOp.SetFlags, "zero flags are ok") -} - -func TestHandleClearFlagsThreeDifferent(t *testing.T) { - options := SetOptions{} - options.ClearFlags = []AccountFlag{1, 2, 4} - - options.handleClearFlags() - - expected := xdr.Uint32(7) - assert.Equal(t, expected, *options.xdrOp.ClearFlags, "three different valid flags are ok") -} - -func TestHandleClearFlagsThreeSame(t *testing.T) { - options := SetOptions{} - options.ClearFlags = []AccountFlag{1, 1, 1} - - options.handleClearFlags() - - expected := xdr.Uint32(1) - assert.Equal(t, expected, *options.xdrOp.ClearFlags, "three of the same valid flags are ok") -} - -func TestHandleClearFlagsRedundantFlagsAllowed(t *testing.T) { - options := SetOptions{} - options.ClearFlags = []AccountFlag{1, 2, 4, 2, 4, 1} - - options.handleClearFlags() - - expected := xdr.Uint32(7) - assert.Equal(t, expected, *options.xdrOp.ClearFlags, "additional redundant flags are allowed") -} - -func TestHandleClearFlagsLessThanThreeAreOK(t *testing.T) { - options := SetOptions{} - options.ClearFlags = []AccountFlag{1, 2} - - options.handleClearFlags() - - expected := xdr.Uint32(3) - assert.Equal(t, expected, *options.xdrOp.ClearFlags, "less than three flags are ok") -} - -func TestHandleClearFlagsInvalidFlagsAllowed(t *testing.T) { - options := SetOptions{} - options.ClearFlags = []AccountFlag{3, 3, 3} - - options.handleClearFlags() - - expected := xdr.Uint32(3) - assert.Equal(t, expected, *options.xdrOp.ClearFlags, "invalid flags are allowed") -} - -func TestHandleClearFlagsZeroFlagsAreOK(t *testing.T) { - options := SetOptions{} - options.ClearFlags = []AccountFlag{0, 2, 0} - - options.handleClearFlags() - - expected := xdr.Uint32(2) - assert.Equal(t, expected, *options.xdrOp.ClearFlags, "zero flags are ok") -} - -func TestEmptyHomeDomainOK(t *testing.T) { - options := SetOptions{ - HomeDomain: NewHomeDomain(""), - } - options.BuildXDR() - - assert.Equal(t, string(*options.xdrOp.HomeDomain), "", "empty string home domain is set") - -} diff --git a/services/horizon/internal/txnbuild/signer_summary.go b/services/horizon/internal/txnbuild/signer_summary.go deleted file mode 100644 index 269e65a8f7..0000000000 --- a/services/horizon/internal/txnbuild/signer_summary.go +++ /dev/null @@ -1,4 +0,0 @@ -package txnbuild - -// SignerSummary is a map of signers to their weights. -type SignerSummary map[string]int32 diff --git a/services/horizon/internal/txnbuild/signers_test.go b/services/horizon/internal/txnbuild/signers_test.go deleted file mode 100644 index 0a9ecb3285..0000000000 --- a/services/horizon/internal/txnbuild/signers_test.go +++ /dev/null @@ -1,558 +0,0 @@ -package txnbuild - -import ( - "github.com/stellar/go/keypair" - "github.com/stellar/go/xdr" - "testing" - - "github.com/stellar/go/network" - "github.com/stretchr/testify/assert" -) - -func TestAccountMergeMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - accountMerge := AccountMerge{ - Destination: "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP", - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&accountMerge}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAIAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAAAAALqLnLFAAAAQAES8MwTufP7l2Rlbg4+1klxAeGgSyTb+vdGI7Or/Lp5xHGZwQ/KvWo0W1ot4hy+WkdJBCD1VF53skB4ZYTPFAnSh2R+AAAAQGPvZk8T2GDp2BpYGeS85VAV2UGKzyjGowt+YOfJwKbW5fjo+GLe47obXEEYxCQDZIsmwG4u5tJ9FUbjuvqi/g0=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestAllowTrustMultSigners(t *testing.T) { - kp0 := newKeypair0() - opSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - txSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - issuedAsset := CreditAsset{"ABCD", kp1.Address()} - allowTrust := AllowTrust{ - Trustor: kp1.Address(), - Type: issuedAsset, - Authorize: true, - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&allowTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAiILoAAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAA4Nxt4XJcrGZRYrUvrOc1sooiQ+QdEk1suS1wo+oucsUAAAAHAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAUFCQ0QAAAABAAAAAAAAAALqLnLFAAAAQHm+8kcSuOMVfthbNRu5ItzonA0ACvL58h4lC6K0JG6OCSR5gRbLUOMqVu1xpQZu+6t9pHwKN9QoEPoXviT3rgDSh2R+AAAAQCr0qzbX9xroeFOzliJgb7+dZJEjyZMpmF3b90NwlEWtm4KPu+U2Lvr91ImeOYtt1/UGksDlGC+3aFq3FsbKBg8=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestBumpSequenceMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - bumpSequence := BumpSequence{ - BumpTo: 9606132444168300, - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&bumpSequence}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAALACIgugAAAGwAAAAAAAAAAuoucsUAAABA5wbLXDFQdTkJ0Oo3mkW6VrcFeylOag0urj6lKXaQV3mGdFQA4J9OezChx5DynW+FxQtuyXbSBYTcgXUADapSCdKHZH4AAABAuuYmmuuwkMBGC3oX4RA6ZkM5PfYrdUuuAhEvOnuanfyynrOgD/RPs0ROOpd7PAOuZiSkWlJZPUCaJTCo8QZdDg==" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestChangeTrustMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - changeTrust := ChangeTrust{ - Line: CreditAsset{"ABCD", kp0.Address()}, - Limit: "10", - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&changeTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAGAAAAAUFCQ0QAAAAA4Nxt4XJcrGZRYrUvrOc1sooiQ+QdEk1suS1wo+oucsUAAAAABfXhAAAAAAAAAAAC6i5yxQAAAEDedJzbQLi0WzudRmn8/iRPOmY8QdhoBM23MZxTiY7A6vGsI8mvVOlWcBKL/x3czQCFFctrZzm0j2zv2Fzj/D4E0odkfgAAAEC08u+7w48yJpcR8MJmK/LKdHlD+wV8xbCo4EgSrjAlJvOXbgPPi6pV2OXHKooA6R2x58og1+uRxoRNmsNhFTwF" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestCreateAccountMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAuoucsUAAABANXxsuIht++BXo21iiKkj0lrhVCYNdbD/uBPbL7AXKoleT1cynaR7luA74npsMfzE9AVFr+VclOY+dzQBqIWZDNKHZH4AAABA0EQ/a/U49VgXN6kAHnxMIfy/7rATGCk+stqym2Pa6fcbIKIFyoTRVi+uPTkIcS0u1wL1FvkWuU4YbfbtUPJ5Aw==" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestCreatePassiveSellOfferMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - createPassiveOffer := CreatePassiveSellOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", kp0.Address()}, - Amount: "10", - Price: "1.0", - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createPassiveOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAEAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAX14QAAAAABAAAAAQAAAAAAAAAC6i5yxQAAAEDQA9vz3Yvc1g/xjJdkyiegE5gw4y1RmGmM6d9Kd+i7FD+i0WdGyzkxf9GjrDprUQj1/iDFGE2HpYOb5Zd5UUcP0odkfgAAAECq+9bggD7neBxaDYO4kxR/ltLjqBucqqAbYcIY7bwnGy32Ca/jvsglwnU2UgX3qhCEHSshN21bsGI/h5f+xcUH" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestInflationMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - inflation := Inflation{ - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&inflation}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAJAAAAAAAAAALqLnLFAAAAQA10jZSBnnrqNR6RH6tbivfJropBxXa2KcH2yN9J3mBGoIxgBSyBCYEc9qKWnkrmZUGvmbTO+LNOd+PdQ4Y8CAPSh2R+AAAAQJ/b1BbMNzsXMGqGELrXH8bcEM5nXa+jq06xrMnIbgMRNRsj+NhSyKifzWP0PQQvHQ4C2yw6y+HBfWvwzAFPhQE=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageDataMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - manageData := ManageData{ - Name: "Fruit preference", - Value: []byte("Apple"), - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&manageData}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAABAAAABUFwcGxlAAAAAAAAAAAAAALqLnLFAAAAQLCtVrhLtpfRGrFhiiGJq891ewFu6ju63po2fgjnYHgA2MgSFJ5rRo3H09uIyWpcmEeeBwXihnx6Ahh9R/pDng7Sh2R+AAAAQOSrmSI0GjSDSgp0+RFwrZYBgqjVtEE5aeCebZ3KR0JCEYXwzIG/q9t1WGz5/zyd8BmmqI1+rP0R6QSp1kDyhwo=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageOfferCreateMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", kp0.Address()} - sellAmount := "100" - price := "0.01" - createOffer, err := CreateOfferOp(selling, buying, sellAmount, price, &opSourceAccount) - check(err) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAALqLnLFAAAAQBo6hZfPKEWgmpWi/TCZonjsQz/w3TCCg2Qcn218b/vCq6OjTezTukCJzJZuhEI7k/STp1/dEptolP9ysGsqegjSh2R+AAAAQLAcnq3rskk4p7shyvfRLuNnK1XgOnVtvho24UW6pqflv+wRaVWJg7Vp848Gi5bBFB8mPJRYMa3lbL78n4wYJA0=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageOfferDeleteMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - offerID := int64(2921622) - deleteOffer, err := DeleteOfferOp(offerID, &opSourceAccount) - check(err) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&deleteOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAADAAAAAAAAAAFGQUtFAAAAAEEHgGTElYZi82AkGiJdSja2OBaU2aEcwwp3AY3tFJ2xAAAAAAAAAAAAAAABAAAAAQAAAAAALJSWAAAAAAAAAALqLnLFAAAAQFp2K2fnu4AY2GsFb5BL8b8uDwCoHvcdHi+e7oedEp524sJCws4nPwEWLFu5DJcMqLCvFkr5UgwtQwFrNJmMqwbSh2R+AAAAQLtQ2CiOJOG5OsmWydFWFUads6QJj51RcJbJb0mCyDewWBpZRLmh45IMyMRlMJoxk8wKJK4UR2Sfolz7aMmjrw4=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageOfferUpdateMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", kp0.Address()} - sellAmount := "50" - price := "0.02" - offerID := int64(2497628) - updateOffer, err := UpdateOfferOp(selling, buying, sellAmount, price, offerID, &opSourceAccount) - check(err) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&updateOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAB3NZQAAAAABAAAAMgAAAAAAJhxcAAAAAAAAAALqLnLFAAAAQOEvekP35V6i3XzbXLdxC5BHFg1pQkhC35KXHJKDYdXwGb5YjHh5amYL78JLtrmswu7NbpWz3MY/rbxFn+8I3gHSh2R+AAAAQLU+5Xee25nSTbRJnWx5zCtkIQ7KzDnb/V/r9nizHsizneito26JQqeEKBH/qz88d3kQxWWC4Lf053tPD6d+SA0=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestPathPaymentMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPayment{ - SendAsset: NativeAsset{}, - SendMax: "10", - Destination: kp0.Address(), - DestAsset: NativeAsset{}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAACAAAAAAAAAAAF9eEAAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAAAmJaAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAALqLnLFAAAAQKYqPzdMAYo7NrrOOE2HnXRzCIBixIT9jWteNysju07WVcGpJhoLJW597UrMlsRVWLB/QJk6e6jw6SzLRDXw5wLSh2R+AAAAQMz9ZqejVTk9KiWZCv0e/hoW+F4ua2mM6tHV/kuzCB9HqVGglbK9xN0aOGnrQwvwlp824cVOYnUkV8+HfwsnQgM=" - - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestPaymentMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - payment := Payment{ - Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", - Amount: "10", - Asset: NativeAsset{}, - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&payment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAABAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAF9eEAAAAAAAAAAALqLnLFAAAAQHzYkZeogiHztanqRvrXXxiNShH/Zf5EUjgabrb6wwgX1eOUBRjp5J92qq8s/o1B1sxrMNiPpViAq40tD/yGfwjSh2R+AAAAQNVC6YLIbAnFs3G/rdf7IxrWYFOxjOKUSZsN0q1Bm/MXk+7ydhcCbYBgq+VGa6eZf8BckgIdAtDI8VNWPoTyhAM=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsMultSigners(t *testing.T) { - kp0 := newKeypair0() - txSourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - kp1 := newKeypair1() - opSourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - setOptions := SetOptions{ - SetFlags: []AccountFlag{AuthRequired, AuthRevocable}, - SourceAccount: &opSourceAccount, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAFAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6i5yxQAAAEA7Wgrkr6q1o1Cf9rzfopqkIUQWD9Se3TagU2GhMn9OjGT75flGAaOdQ+kHLDGQjThDKMMdB8jCJGe8IGc/dIQP0odkfgAAAEDni8seENXmyh0QgHkLjM4EmhHmBr5NvU6VpJaVBfv631yaaHP7lONfg9x8DyHjz8uh03S7ipShHIrQDFN+L+cM" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -type SignatureList interface { - Signatures() []xdr.DecoratedSignature - Hash(networkStr string) ([32]byte, error) -} - -func verifySignatures(t *testing.T, tx SignatureList, signers ...*keypair.Full) { - assert.Len(t, tx.Signatures(), len(signers)) - - hash, err := tx.Hash(network.TestNetworkPassphrase) - assert.NoError(t, err) - signatures := tx.Signatures() - for i, kp := range signers { - assert.NoError(t, kp.Verify(hash[:], signatures[i].Signature)) - } -} - -func TestSigningImmutability(t *testing.T) { - kp0, kp1, kp2 := newKeypair0(), newKeypair1(), newKeypair2() - - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - params := TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - } - root, err := NewTransaction(params) - assert.NoError(t, err) - root, err = root.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - rootB64, err := root.Base64() - assert.NoError(t, err) - - left, err := root.Sign(network.TestNetworkPassphrase, kp1) - assert.NoError(t, err) - leftB64, err := left.Base64() - assert.NoError(t, err) - - right, err := root.Sign(network.TestNetworkPassphrase, kp2) - assert.NoError(t, err) - rightB64, err := right.Base64() - assert.NoError(t, err) - - expectedRootB64, err := newSignedTransaction( - params, network.TestNetworkPassphrase, kp0, - ) - assert.NoError(t, err) - expectedLeftB64, err := newSignedTransaction( - params, network.TestNetworkPassphrase, kp0, kp1, - ) - assert.NoError(t, err) - expectedRightB64, err := newSignedTransaction( - params, network.TestNetworkPassphrase, kp0, kp2, - ) - assert.NoError(t, err) - - assert.Equal(t, expectedRootB64, rootB64) - verifySignatures(t, root, kp0) - assert.Equal(t, expectedLeftB64, leftB64) - verifySignatures(t, left, kp0, kp1) - assert.Equal(t, expectedRightB64, rightB64) - verifySignatures(t, right, kp0, kp2) -} - -func TestFeeBumpSigningImmutability(t *testing.T) { - kp0, kp1, kp2 := newKeypair0(), newKeypair1(), newKeypair2() - - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - innerParams := TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - } - inner, err := NewTransaction(innerParams) - assert.NoError(t, err) - convertToV1Tx(inner) - - params := FeeBumpTransactionParams{ - Inner: inner, - FeeAccount: kp1.Address(), - BaseFee: MinBaseFee, - } - root, err := NewFeeBumpTransaction(params) - assert.NoError(t, err) - root, err = root.Sign(network.TestNetworkPassphrase, kp1) - rootB64, err := root.Base64() - assert.NoError(t, err) - - left, err := root.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - leftB64, err := left.Base64() - assert.NoError(t, err) - - right, err := root.Sign(network.TestNetworkPassphrase, kp2) - assert.NoError(t, err) - rightB64, err := right.Base64() - assert.NoError(t, err) - - expectedRootB64, err := newSignedFeeBumpTransaction( - params, network.TestNetworkPassphrase, kp1, - ) - assert.NoError(t, err) - - expectedLeftB64, err := newSignedFeeBumpTransaction( - params, network.TestNetworkPassphrase, kp1, kp0, - ) - assert.NoError(t, err) - expectedRightB64, err := newSignedFeeBumpTransaction( - params, network.TestNetworkPassphrase, kp1, kp2, - ) - assert.NoError(t, err) - - assert.Equal(t, expectedRootB64, rootB64) - verifySignatures(t, root, kp1) - assert.Equal(t, expectedLeftB64, leftB64) - verifySignatures(t, left, kp1, kp0) - assert.Equal(t, expectedRightB64, rightB64) - verifySignatures(t, right, kp1, kp2) -} diff --git a/services/horizon/internal/txnbuild/simple_account.go b/services/horizon/internal/txnbuild/simple_account.go deleted file mode 100644 index 0c44b03ee6..0000000000 --- a/services/horizon/internal/txnbuild/simple_account.go +++ /dev/null @@ -1,32 +0,0 @@ -package txnbuild - -// SimpleAccount is a minimal implementation of an Account. -type SimpleAccount struct { - AccountID string - Sequence int64 -} - -// GetAccountID returns the Account ID. -func (sa *SimpleAccount) GetAccountID() string { - return sa.AccountID -} - -// IncrementSequenceNumber increments the internal record of the -// account's sequence number by 1. -func (sa *SimpleAccount) IncrementSequenceNumber() (int64, error) { - sa.Sequence++ - return sa.Sequence, nil -} - -// GetSequenceNumber returns the sequence number of the account. -func (sa *SimpleAccount) GetSequenceNumber() (int64, error) { - return sa.Sequence, nil -} - -// NewSimpleAccount is a factory method that creates a SimpleAccount from "accountID" and "sequence". -func NewSimpleAccount(accountID string, sequence int64) SimpleAccount { - return SimpleAccount{accountID, sequence} -} - -// ensure that SimpleAccount implements Account interface. -var _ Account = &SimpleAccount{} diff --git a/services/horizon/internal/txnbuild/timebounds.go b/services/horizon/internal/txnbuild/timebounds.go deleted file mode 100644 index ce1a0b66e7..0000000000 --- a/services/horizon/internal/txnbuild/timebounds.go +++ /dev/null @@ -1,71 +0,0 @@ -package txnbuild - -import ( - "errors" - "time" -) - -// TimeoutInfinite allows an indefinite upper bound to be set for Transaction.MaxTime. This is usually not -// what you want. -const TimeoutInfinite = int64(0) - -// Timebounds represents the time window during which a Stellar transaction is considered valid. -// -// MinTime and MaxTime represent Stellar timebounds - a window of time over which the Transaction will be -// considered valid. In general, almost all Transactions benefit from setting an upper timebound, because once submitted, -// the status of a pending Transaction may remain unresolved for a long time if the network is congested. -// With an upper timebound, the submitter has a guaranteed time at which the Transaction is known to have either -// succeeded or failed, and can then take appropriate action (e.g. to resubmit or mark as resolved). -// -// Create a Timebounds struct using one of NewTimebounds(), NewTimeout(), or NewInfiniteTimeout(). -type Timebounds struct { - MinTime int64 - MaxTime int64 - wasBuilt bool -} - -// Validate for Timebounds sanity-checks the configured Timebound limits, and confirms the object was built -// using a factory method. This is done to ensure that default Timebound structs (which have no limits) are not -// valid - you must explicitly specifiy the Timebound you require. -func (tb *Timebounds) Validate() error { - if !tb.wasBuilt { - return errors.New("timebounds must be constructed using NewTimebounds(), NewTimeout(), or NewInfiniteTimeout()") - } - if tb.MinTime < 0 { - return errors.New("invalid timebound: minTime cannot be negative") - } - - if tb.MaxTime < 0 { - return errors.New("invalid timebound: maxTime cannot be negative") - } - - if tb.MaxTime != TimeoutInfinite { - if tb.MaxTime < tb.MinTime { - return errors.New("invalid timebound: maxTime < minTime") - } - } - - return nil -} - -// NewTimebounds is a factory method that constructs a Timebounds object from a min and max time. -// A Transaction cannot be built unless a Timebounds object is provided through a factory method. -func NewTimebounds(minTime, maxTime int64) Timebounds { - return Timebounds{minTime, maxTime, true} -} - -// NewTimeout is a factory method that sets the MaxTime to be the duration in seconds in the -// future specified by 'timeout'. -// A Transaction cannot be built unless a Timebounds object is provided through a factory method. -// This method uses the provided system time - make sure it is accurate. -func NewTimeout(timeout int64) Timebounds { - return Timebounds{0, time.Now().UTC().Unix() + timeout, true} -} - -// NewInfiniteTimeout is a factory method that sets the MaxTime to a value representing an indefinite -// upper time bound. This is rarely needed, but is helpful for certain smart contracts, and for -// deterministic testing. A Transaction cannot be built unless a Timebounds object is provided through -// a factory method. -func NewInfiniteTimeout() Timebounds { - return Timebounds{0, TimeoutInfinite, true} -} diff --git a/services/horizon/internal/txnbuild/timebounds_test.go b/services/horizon/internal/txnbuild/timebounds_test.go deleted file mode 100644 index b852242e66..0000000000 --- a/services/horizon/internal/txnbuild/timebounds_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package txnbuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTimeboundsRequireConstructor(t *testing.T) { - tb := Timebounds{MinTime: -1, MaxTime: 300} - err := tb.Validate() - expectedErrMsg := "timebounds must be constructed using NewTimebounds(), NewTimeout(), or NewInfiniteTimeout()" - - require.EqualError(t, err, expectedErrMsg, "Default timebounds not allowed") -} - -func TestSetTimeboundsNegativeMinTime(t *testing.T) { - tb := NewTimebounds(-1, 300) - err := tb.Validate() - expectedErrMsg := "invalid timebound: minTime cannot be negative" - - require.EqualError(t, err, expectedErrMsg, "No negative minTime allowed") -} - -func TestSetTimeboundsNegativeMaxTime(t *testing.T) { - tb := NewTimebounds(1, -300) - err := tb.Validate() - expectedErrMsg := "invalid timebound: maxTime cannot be negative" - - require.EqualError(t, err, expectedErrMsg, "No negative maxTime allowed") -} - -func TestSetTimeoutNegativeWidth(t *testing.T) { - tb := NewTimeout(300) - tb.MinTime = 5555624032 // Sometime in 2146 - err := tb.Validate() - expectedErrMsg := "invalid timebound: maxTime < minTime" - - require.EqualError(t, err, expectedErrMsg, "No negative width windows") -} - -func TestSetTimeout(t *testing.T) { - tb := NewTimeout(300) - tb.MinTime = 1 - err := tb.Validate() - if assert.NoError(t, err) { - assert.Equal(t, int64(1), tb.MinTime) - assert.NotNil(t, tb.MaxTime) - } -} diff --git a/services/horizon/internal/txnbuild/transaction.go b/services/horizon/internal/txnbuild/transaction.go deleted file mode 100644 index 836d58ab22..0000000000 --- a/services/horizon/internal/txnbuild/transaction.go +++ /dev/null @@ -1,1062 +0,0 @@ -/* -Package txnbuild implements transactions and operations on the Stellar network. -This library provides an interface to the Stellar transaction model. It supports the building of Go applications on -top of the Stellar network (https://www.stellar.org/). Transactions constructed by this library may be submitted -to any Horizon instance for processing onto the ledger, using any Stellar SDK client. The recommended client for Go -programmers is horizonclient (https://github.com/stellar/go/tree/master/clients/horizonclient). Together, these two -libraries provide a complete Stellar SDK. -For more information and further examples, see https://www.stellar.org/developers/go/reference/index.html. -*/ -package txnbuild - -import ( - "bytes" - "crypto/rand" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "fmt" - "math" - "math/bits" - "strings" - "time" - - "github.com/stellar/go/keypair" - "github.com/stellar/go/network" - "github.com/stellar/go/strkey" - "github.com/stellar/go/support/errors" - "github.com/stellar/go/xdr" -) - -// MinBaseFee is the minimum transaction fee for the Stellar network. -const MinBaseFee = 100 - -// Account represents the aspects of a Stellar account necessary to construct transactions. See -// https://www.stellar.org/developers/guides/concepts/accounts.html -type Account interface { - GetAccountID() string - IncrementSequenceNumber() (int64, error) - GetSequenceNumber() (int64, error) -} - -func hashHex(e xdr.TransactionEnvelope, networkStr string) (string, error) { - h, err := network.HashTransactionInEnvelope(e, networkStr) - if err != nil { - return "", err - } - return hex.EncodeToString(h[:]), nil -} - -func concatSignatures( - e xdr.TransactionEnvelope, - networkStr string, - signatures []xdr.DecoratedSignature, - kps ...*keypair.Full, -) ([]xdr.DecoratedSignature, error) { - // Hash the transaction - h, err := network.HashTransactionInEnvelope(e, networkStr) - if err != nil { - return nil, errors.Wrap(err, "failed to hash transaction") - } - - extended := make( - []xdr.DecoratedSignature, - len(signatures), - len(signatures)+len(kps), - ) - copy(extended, signatures) - // Sign the hash - for _, kp := range kps { - sig, err := kp.SignDecorated(h[:]) - if err != nil { - return nil, errors.Wrap(err, "failed to sign transaction") - } - extended = append(extended, sig) - } - return extended, nil -} - -func concatSignatureBase64(e xdr.TransactionEnvelope, signatures []xdr.DecoratedSignature, networkStr, publicKey, signature string) ([]xdr.DecoratedSignature, error) { - if signature == "" { - return nil, errors.New("signature not presented") - } - - kp, err := keypair.ParseAddress(publicKey) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse the public key %s", publicKey) - } - - sigBytes, err := base64.StdEncoding.DecodeString(signature) - if err != nil { - return nil, errors.Wrapf(err, "failed to base64-decode the signature %s", signature) - } - - h, err := network.HashTransactionInEnvelope(e, networkStr) - if err != nil { - return nil, errors.Wrap(err, "failed to hash transaction") - } - - err = kp.Verify(h[:], sigBytes) - if err != nil { - return nil, errors.Wrap(err, "failed to verify the signature") - } - - extended := make([]xdr.DecoratedSignature, len(signatures), len(signatures)+1) - copy(extended, signatures) - extended = append(extended, xdr.DecoratedSignature{ - Hint: xdr.SignatureHint(kp.Hint()), - Signature: xdr.Signature(sigBytes), - }) - - return extended, nil -} - -func stringsToKP(keys ...string) ([]*keypair.Full, error) { - var signers []*keypair.Full - for _, k := range keys { - kp, err := keypair.Parse(k) - if err != nil { - return nil, errors.Wrapf(err, "provided string %s is not a valid Stellar key", k) - } - kpf, ok := kp.(*keypair.Full) - if !ok { - return nil, errors.New("provided string %s is not a valid Stellar secret key") - } - signers = append(signers, kpf) - } - - return signers, nil -} - -func concatHashX(signatures []xdr.DecoratedSignature, preimage []byte) ([]xdr.DecoratedSignature, error) { - if maxSize := xdr.Signature(preimage).XDRMaxSize(); len(preimage) > maxSize { - return nil, errors.Errorf( - "preimage cannnot be more than %d bytes", maxSize, - ) - } - extended := make( - []xdr.DecoratedSignature, - len(signatures), - len(signatures)+1, - ) - copy(extended, signatures) - - preimageHash := sha256.Sum256(preimage) - var hint [4]byte - // copy the last 4-bytes of the signer public key to be used as hint - copy(hint[:], preimageHash[28:]) - - sig := xdr.DecoratedSignature{ - Hint: xdr.SignatureHint(hint), - Signature: xdr.Signature(preimage), - } - return append(extended, sig), nil -} - -func marshallBinary(e xdr.TransactionEnvelope, signatures []xdr.DecoratedSignature) ([]byte, error) { - switch e.Type { - case xdr.EnvelopeTypeEnvelopeTypeTx: - e.V1.Signatures = signatures - case xdr.EnvelopeTypeEnvelopeTypeTxV0: - e.V0.Signatures = signatures - case xdr.EnvelopeTypeEnvelopeTypeTxFeeBump: - e.FeeBump.Signatures = signatures - default: - panic("invalid transaction type: " + e.Type.String()) - } - - var txBytes bytes.Buffer - _, err := xdr.Marshal(&txBytes, e) - if err != nil { - return nil, err - } - return txBytes.Bytes(), nil -} - -func marshallBase64(e xdr.TransactionEnvelope, signatures []xdr.DecoratedSignature) (string, error) { - binary, err := marshallBinary(e, signatures) - if err != nil { - return "", errors.Wrap(err, "failed to get XDR bytestring") - } - - return base64.StdEncoding.EncodeToString(binary), nil -} - -func cloneEnvelope(e xdr.TransactionEnvelope, signatures []xdr.DecoratedSignature) (xdr.TransactionEnvelope, error) { - var clone xdr.TransactionEnvelope - binary, err := marshallBinary(e, signatures) - if err != nil { - return clone, errors.Wrap(err, "could not marshall envelope") - } - - if err = xdr.SafeUnmarshal(binary, &clone); err != nil { - return clone, errors.Wrap(err, "could not unmarshall envelope") - } - return clone, nil -} - -// Transaction represents a Stellar transaction. See -// https://www.stellar.org/developers/guides/concepts/transactions.html -// A Transaction may be wrapped by a FeeBumpTransaction in which case -// the account authorizing the FeeBumpTransaction will pay for the transaction fees -// instead of the Transaction's source account. -type Transaction struct { - envelope xdr.TransactionEnvelope - baseFee int64 - maxFee int64 - sourceAccount SimpleAccount - operations []Operation - memo Memo - timebounds Timebounds - signatures []xdr.DecoratedSignature -} - -// BaseFee returns the per operation fee for this transaction. -func (t *Transaction) BaseFee() int64 { - return t.baseFee -} - -// MaxFee returns the total fees which can be spent to submit this transaction. -func (t *Transaction) MaxFee() int64 { - return t.maxFee -} - -// SourceAccount returns the account which is originating this account. -func (t *Transaction) SourceAccount() SimpleAccount { - return t.sourceAccount -} - -// Memo returns the memo configured for this transaction. -func (t *Transaction) Memo() Memo { - return t.memo -} - -// Timebounds returns the Timebounds configured for this transaction. -func (t *Transaction) Timebounds() Timebounds { - return t.timebounds -} - -// Operations returns the list of operations included in this transaction. -// The contents of the returned slice should not be modified. -func (t *Transaction) Operations() []Operation { - return t.operations -} - -// Signatures returns the list of signatures attached to this transaction. -// The contents of the returned slice should not be modified. -func (t *Transaction) Signatures() []xdr.DecoratedSignature { - return t.signatures -} - -// Hash returns the network specific hash of this transaction -// encoded as a byte array. -func (t *Transaction) Hash(networkStr string) ([32]byte, error) { - return network.HashTransactionInEnvelope(t.envelope, networkStr) -} - -// HashHex returns the network specific hash of this transaction -// encoded as a hexadecimal string. -func (t *Transaction) HashHex(network string) (string, error) { - return hashHex(t.envelope, network) -} - -// Sign returns a new Transaction instance which extends the current instance -// with additional signatures derived from the given list of keypair instances. -func (t *Transaction) Sign(network string, kps ...*keypair.Full) (*Transaction, error) { - extendedSignatures, err := concatSignatures(t.envelope, network, t.signatures, kps...) - if err != nil { - return nil, err - } - - newTx := new(Transaction) - *newTx = *t - newTx.signatures = extendedSignatures - return newTx, nil -} - -// SignWithKeyString returns a new Transaction instance which extends the current instance -// with additional signatures derived from the given list of private key strings. -func (t *Transaction) SignWithKeyString(network string, keys ...string) (*Transaction, error) { - kps, err := stringsToKP(keys...) - if err != nil { - return nil, err - } - return t.Sign(network, kps...) -} - -// SignHashX returns a new Transaction instance which extends the current instance -// with HashX signature type. -// See description here: https://www.stellar.org/developers/guides/concepts/multi-sig.html#hashx. -func (t *Transaction) SignHashX(preimage []byte) (*Transaction, error) { - extendedSignatures, err := concatHashX(t.signatures, preimage) - if err != nil { - return nil, err - } - - newTx := new(Transaction) - *newTx = *t - newTx.signatures = extendedSignatures - return newTx, nil -} - -// AddSignatureBase64 returns a new Transaction instance which extends the current instance -// with an additional signature derived from the given base64-encoded signature. -func (t *Transaction) AddSignatureBase64(network, publicKey, signature string) (*Transaction, error) { - extendedSignatures, err := concatSignatureBase64(t.envelope, t.signatures, network, publicKey, signature) - if err != nil { - return nil, err - } - - newTx := new(Transaction) - *newTx = *t - newTx.signatures = extendedSignatures - return newTx, nil -} - -// TxEnvelope returns the a xdr.TransactionEnvelope instance which is -// equivalent to this transaction. -func (t *Transaction) TxEnvelope() (xdr.TransactionEnvelope, error) { - return cloneEnvelope(t.envelope, t.signatures) -} - -// MarshalBinary returns the binary XDR representation of the transaction envelope. -func (t *Transaction) MarshalBinary() ([]byte, error) { - return marshallBinary(t.envelope, t.signatures) -} - -// Base64 returns the base 64 XDR representation of the transaction envelope. -func (t *Transaction) Base64() (string, error) { - return marshallBase64(t.envelope, t.signatures) -} - -// FeeBumpTransaction represents a CAP 15 fee bump transaction. -// Fee bump transactions allow an arbitrary account to pay the fee for a transaction. -type FeeBumpTransaction struct { - envelope xdr.TransactionEnvelope - baseFee int64 - maxFee int64 - feeAccount string - inner *Transaction - signatures []xdr.DecoratedSignature -} - -// BaseFee returns the per operation fee for this transaction. -func (t *FeeBumpTransaction) BaseFee() int64 { - return t.baseFee -} - -// MaxFee returns the total fees which can be spent to submit this transaction. -func (t *FeeBumpTransaction) MaxFee() int64 { - return t.maxFee -} - -// FeeAccount returns the address of the account which will be paying for the inner transaction. -func (t *FeeBumpTransaction) FeeAccount() string { - return t.feeAccount -} - -// Signatures returns the list of signatures attached to this transaction. -// The contents of the returned slice should not be modified. -func (t *FeeBumpTransaction) Signatures() []xdr.DecoratedSignature { - return t.signatures -} - -// Hash returns the network specific hash of this transaction -// encoded as a byte array. -func (t *FeeBumpTransaction) Hash(networkStr string) ([32]byte, error) { - return network.HashTransactionInEnvelope(t.envelope, networkStr) -} - -// Sign returns a new FeeBumpTransaction instance which extends the current instance -// with additional signatures derived from the given list of keypair instances. -func (t *FeeBumpTransaction) Sign(network string, kps ...*keypair.Full) (*FeeBumpTransaction, error) { - extendedSignatures, err := concatSignatures(t.envelope, network, t.signatures, kps...) - if err != nil { - return nil, err - } - - newTx := new(FeeBumpTransaction) - *newTx = *t - newTx.signatures = extendedSignatures - return newTx, nil -} - -// TxEnvelope returns the a xdr.TransactionEnvelope instance which is -// equivalent to this transaction. -func (t *FeeBumpTransaction) TxEnvelope() (xdr.TransactionEnvelope, error) { - return cloneEnvelope(t.envelope, t.signatures) -} - -// MarshalBinary returns the binary XDR representation of the transaction envelope. -func (t *FeeBumpTransaction) MarshalBinary() ([]byte, error) { - return marshallBinary(t.envelope, t.signatures) -} - -// Base64 returns the base 64 XDR representation of the transaction envelope. -func (t *FeeBumpTransaction) Base64() (string, error) { - return marshallBase64(t.envelope, t.signatures) -} - -// InnerTransaction returns the Transaction which is wrapped by -// this FeeBumpTransaction instance. -func (t *FeeBumpTransaction) InnerTransaction() *Transaction { - innerCopy := new(Transaction) - *innerCopy = *t.inner - return innerCopy -} - -// GenericTransaction represents a parsed transaction envelope returned by TransactionFromXDR. -// A GenericTransaction can be either a Transaction or a FeeBumpTransaction. -type GenericTransaction struct { - simple *Transaction - feeBump *FeeBumpTransaction -} - -// Transaction unpacks the GenericTransaction instance into a Transaction. -// The function also returns a boolean which is true if the GenericTransaction can be -// unpacked into a Transaction. -func (t GenericTransaction) Transaction() (*Transaction, bool) { - return t.simple, t.simple != nil -} - -// FeeBump unpacks the GenericTransaction instance into a FeeBumpTransaction. -// The function also returns a boolean which is true if the GenericTransaction -// can be unpacked into a FeeBumpTransaction. -func (t GenericTransaction) FeeBump() (*FeeBumpTransaction, bool) { - return t.feeBump, t.feeBump != nil -} - -// TransactionFromXDR parses the supplied transaction envelope in base64 XDR -// and returns a GenericTransaction instance. -func TransactionFromXDR(txeB64 string) (*GenericTransaction, error) { - var xdrEnv xdr.TransactionEnvelope - err := xdr.SafeUnmarshalBase64(txeB64, &xdrEnv) - if err != nil { - return nil, errors.Wrap(err, "unable to unmarshal transaction envelope") - } - return transactionFromParsedXDR(xdrEnv) -} - -func transactionFromParsedXDR(xdrEnv xdr.TransactionEnvelope) (*GenericTransaction, error) { - var err error - newTx := &GenericTransaction{} - - if xdrEnv.IsFeeBump() { - var innerTx *GenericTransaction - innerTx, err = transactionFromParsedXDR(xdr.TransactionEnvelope{ - Type: xdr.EnvelopeTypeEnvelopeTypeTx, - V1: xdrEnv.FeeBump.Tx.InnerTx.V1, - }) - if err != nil { - return newTx, errors.New("could not parse inner transaction") - } - feeBumpAccount := xdrEnv.FeeBumpAccount().ToAccountId() - newTx.feeBump = &FeeBumpTransaction{ - envelope: xdrEnv, - // A fee-bump transaction has an effective number of operations equal to one plus the - // number of operations in the inner transaction. Correspondingly, the minimum fee for - // the fee-bump transaction is one base fee more than the minimum fee for the inner - // transaction. - baseFee: xdrEnv.FeeBumpFee() / int64(len(innerTx.simple.operations)+1), - maxFee: xdrEnv.FeeBumpFee(), - inner: innerTx.simple, - feeAccount: feeBumpAccount.Address(), - signatures: xdrEnv.FeeBumpSignatures(), - } - return newTx, nil - } - - sourceAccount := xdrEnv.SourceAccount().ToAccountId() - - totalFee := int64(xdrEnv.Fee()) - baseFee := totalFee - if count := int64(len(xdrEnv.Operations())); count > 0 { - baseFee = baseFee / count - } - - newTx.simple = &Transaction{ - envelope: xdrEnv, - baseFee: baseFee, - maxFee: totalFee, - sourceAccount: SimpleAccount{ - AccountID: sourceAccount.Address(), - Sequence: xdrEnv.SeqNum(), - }, - operations: nil, - memo: nil, - timebounds: Timebounds{}, - signatures: xdrEnv.Signatures(), - } - - if timeBounds := xdrEnv.TimeBounds(); timeBounds != nil { - newTx.simple.timebounds = NewTimebounds(int64(timeBounds.MinTime), int64(timeBounds.MaxTime)) - } - - newTx.simple.memo, err = memoFromXDR(xdrEnv.Memo()) - if err != nil { - return nil, errors.Wrap(err, "unable to parse memo") - } - - operations := xdrEnv.Operations() - for _, op := range operations { - newOp, err := operationFromXDR(op) - if err != nil { - return nil, err - } - newTx.simple.operations = append(newTx.simple.operations, newOp) - } - - return newTx, nil -} - -// TransactionParams is a container for parameters -// which are used to construct new Transaction instances -type TransactionParams struct { - SourceAccount Account - IncrementSequenceNum bool - Operations []Operation - BaseFee int64 - Memo Memo - Timebounds Timebounds -} - -// NewTransaction returns a new Transaction instance -func NewTransaction(params TransactionParams) (*Transaction, error) { - var sequence int64 - var err error - - if params.SourceAccount == nil { - return nil, errors.New("transaction has no source account") - } - - if params.IncrementSequenceNum { - sequence, err = params.SourceAccount.IncrementSequenceNumber() - } else { - sequence, err = params.SourceAccount.GetSequenceNumber() - } - if err != nil { - return nil, errors.Wrap(err, "could not obtain account sequence") - } - - tx := &Transaction{ - baseFee: params.BaseFee, - sourceAccount: SimpleAccount{ - AccountID: params.SourceAccount.GetAccountID(), - Sequence: sequence, - }, - operations: params.Operations, - memo: params.Memo, - timebounds: params.Timebounds, - signatures: nil, - } - - accountID, err := xdr.AddressToAccountId(tx.sourceAccount.AccountID) - if err != nil { - return nil, errors.Wrap(err, "account id is not valid") - } - - sourceAccountEd25519, ok := accountID.GetEd25519() - if !ok { - return nil, errors.New("invalid account id") - } - if tx.baseFee < MinBaseFee { - return nil, errors.Errorf( - "base fee cannot be lower than network minimum of %d", MinBaseFee, - ) - } - - if len(tx.operations) == 0 { - return nil, errors.New("transaction has no operations") - } - - // check if maxFee fits in a uint32 - // 64 bit fees are only available in fee bump transactions - // if maxFee is negative then there must have been an int overflow - hi, lo := bits.Mul64(uint64(params.BaseFee), uint64(len(params.Operations))) - if hi > 0 || lo > math.MaxUint32 { - return nil, errors.Errorf("base fee %d results in an overflow of max fee", params.BaseFee) - } - tx.maxFee = int64(lo) - - // Check and set the timebounds - err = tx.timebounds.Validate() - if err != nil { - return nil, errors.Wrap(err, "invalid time bounds") - } - - envelope := xdr.TransactionEnvelope{ - Type: xdr.EnvelopeTypeEnvelopeTypeTxV0, - V0: &xdr.TransactionV0Envelope{ - Tx: xdr.TransactionV0{ - SourceAccountEd25519: sourceAccountEd25519, - Fee: xdr.Uint32(tx.maxFee), - SeqNum: xdr.SequenceNumber(sequence), - TimeBounds: &xdr.TimeBounds{ - MinTime: xdr.TimePoint(tx.timebounds.MinTime), - MaxTime: xdr.TimePoint(tx.timebounds.MaxTime), - }, - }, - Signatures: nil, - }, - } - - // Handle the memo, if one is present - if tx.memo != nil { - xdrMemo, err := tx.memo.ToXDR() - if err != nil { - return nil, errors.Wrap(err, "couldn't build memo XDR") - } - envelope.V0.Tx.Memo = xdrMemo - } - - for _, op := range tx.operations { - if verr := op.Validate(); verr != nil { - return nil, errors.Wrap(verr, fmt.Sprintf("validation failed for %T operation", op)) - } - - xdrOperation, err2 := op.BuildXDR() - if err2 != nil { - return nil, errors.Wrap(err2, fmt.Sprintf("failed to build operation %T", op)) - } - envelope.V0.Tx.Operations = append(envelope.V0.Tx.Operations, xdrOperation) - } - - tx.envelope = envelope - return tx, nil -} - -// FeeBumpTransactionParams is a container for parameters -// which are used to construct new FeeBumpTransaction instances -type FeeBumpTransactionParams struct { - Inner *Transaction - FeeAccount string - BaseFee int64 -} - -// NewFeeBumpTransaction returns a new FeeBumpTransaction instance -func NewFeeBumpTransaction(params FeeBumpTransactionParams) (*FeeBumpTransaction, error) { - if params.Inner == nil { - return nil, errors.New("inner transaction is missing") - } - - tx := &FeeBumpTransaction{ - baseFee: params.BaseFee, - // A fee-bump transaction has an effective number of operations equal to one plus the - // number of operations in the inner transaction. Correspondingly, the minimum fee for - // the fee-bump transaction is one base fee more than the minimum fee for the inner - // transaction. - maxFee: params.BaseFee * int64(len(params.Inner.operations)+1), - feeAccount: params.FeeAccount, - inner: new(Transaction), - } - *tx.inner = *params.Inner - - hi, lo := bits.Mul64(uint64(params.BaseFee), uint64(len(params.Inner.operations)+1)) - if hi > 0 || lo > math.MaxInt64 { - return nil, errors.Errorf("base fee %d results in an overflow of max fee", params.BaseFee) - } - tx.maxFee = int64(lo) - - if tx.baseFee < tx.inner.baseFee { - return tx, errors.New("base fee cannot be lower than provided inner transaction fee") - } - if tx.baseFee < MinBaseFee { - return tx, errors.Errorf( - "base fee cannot be lower than network minimum of %d", MinBaseFee, - ) - } - - accountID, err := xdr.AddressToAccountId(tx.feeAccount) - if err != nil { - return tx, errors.Wrap(err, "fee account is not a valid address") - } - - innerEnv, err := tx.inner.TxEnvelope() - if err != nil { - return tx, errors.Wrap(err, "inner transaction envelope not found") - } - if innerEnv.Type != xdr.EnvelopeTypeEnvelopeTypeTx { - return tx, errors.Errorf("%v transactions cannot be fee bumped", innerEnv.Type.String()) - } - - tx.envelope = xdr.TransactionEnvelope{ - Type: xdr.EnvelopeTypeEnvelopeTypeTxFeeBump, - FeeBump: &xdr.FeeBumpTransactionEnvelope{ - Tx: xdr.FeeBumpTransaction{ - FeeSource: accountID.ToMuxedAccount(), - Fee: xdr.Int64(tx.maxFee), - InnerTx: xdr.FeeBumpTransactionInnerTx{ - Type: xdr.EnvelopeTypeEnvelopeTypeTx, - V1: innerEnv.V1, - }, - }, - }, - } - - return tx, nil -} - -// BuildChallengeTx is a factory method that creates a valid SEP 10 challenge, for use in web authentication. -// "timebound" is the time duration the transaction should be valid for, and must be greater than 1s (300s is recommended). -// More details on SEP 10: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md -func BuildChallengeTx(serverSignerSecret, clientAccountID, anchorName, network string, timebound time.Duration) (*Transaction, error) { - if timebound < time.Second { - return nil, errors.New("provided timebound must be at least 1s (300s is recommended)") - } - - serverKP, err := keypair.Parse(serverSignerSecret) - if err != nil { - return nil, err - } - - // SEP10 spec requires 48 byte cryptographic-quality random string - randomNonce, err := generateRandomNonce(48) - if err != nil { - return nil, err - } - // Encode 48-byte nonce to base64 for a total of 64-bytes - randomNonceToString := base64.StdEncoding.EncodeToString(randomNonce) - if len(randomNonceToString) != 64 { - return nil, errors.New("64 byte long random nonce required") - } - - if _, err = xdr.AddressToAccountId(clientAccountID); err != nil { - return nil, errors.Wrapf(err, "%s is not a valid account id", clientAccountID) - } - - // represent server signing account as SimpleAccount - sa := SimpleAccount{ - AccountID: serverKP.Address(), - Sequence: 0, - } - - // represent client account as SimpleAccount - ca := SimpleAccount{ - AccountID: clientAccountID, - } - - currentTime := time.Now().UTC() - maxTime := currentTime.Add(timebound) - - // Create a SEP 10 compatible response. See - // https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#response - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sa, - IncrementSequenceNum: false, - Operations: []Operation{ - &ManageData{ - SourceAccount: &ca, - Name: anchorName + " auth", - Value: []byte(randomNonceToString), - }, - }, - BaseFee: MinBaseFee, - Memo: nil, - Timebounds: NewTimebounds(currentTime.Unix(), maxTime.Unix()), - }, - ) - if err != nil { - return nil, err - } - tx, err = tx.Sign(network, serverKP.(*keypair.Full)) - if err != nil { - return nil, err - } - - return tx, nil -} - -// generateRandomNonce creates a cryptographically secure random slice of `n` bytes. -func generateRandomNonce(n int) ([]byte, error) { - binary := make([]byte, n) - _, err := rand.Read(binary) - - if err != nil { - return []byte{}, err - } - - return binary, err -} - -// ReadChallengeTx reads a SEP 10 challenge transaction and returns the decoded -// transaction and client account ID contained within. -// -// It also verifies that transaction is signed by the server. -// -// It does not verify that the transaction has been signed by the client or -// that any signatures other than the servers on the transaction are valid. Use -// one of the following functions to completely verify the transaction: -// - VerifyChallengeTxThreshold -// - VerifyChallengeTxSigners -func ReadChallengeTx(challengeTx, serverAccountID, network string) (tx *Transaction, clientAccountID string, err error) { - parsed, err := TransactionFromXDR(challengeTx) - if err != nil { - return tx, clientAccountID, errors.Wrap(err, "could not parse challenge") - } - - var isSimple bool - tx, isSimple = parsed.Transaction() - if !isSimple { - return tx, clientAccountID, errors.New("challenge cannot be a fee bump transaction") - } - - // Enforce no muxed accounts (at least until we understand their impact) - if tx.envelope.SourceAccount().Type == xdr.CryptoKeyTypeKeyTypeMuxedEd25519 { - err = errors.New("invalid source account: only valid Ed25519 accounts are allowed in challenge transactions") - return tx, clientAccountID, err - } - - // verify transaction source - if tx.SourceAccount().AccountID != serverAccountID { - return tx, clientAccountID, errors.New("transaction source account is not equal to server's account") - } - - // verify sequence number - if tx.SourceAccount().Sequence != 0 { - return tx, clientAccountID, errors.New("transaction sequence number must be 0") - } - - // verify timebounds - if tx.Timebounds().MaxTime == TimeoutInfinite { - return tx, clientAccountID, errors.New("transaction requires non-infinite timebounds") - } - currentTime := time.Now().UTC().Unix() - if currentTime < tx.Timebounds().MinTime || currentTime > tx.Timebounds().MaxTime { - return tx, clientAccountID, errors.Errorf("transaction is not within range of the specified timebounds (currentTime=%d, MinTime=%d, MaxTime=%d)", - currentTime, tx.Timebounds().MinTime, tx.Timebounds().MaxTime) - } - - // verify operation - operations := tx.Operations() - if len(operations) != 1 { - return tx, clientAccountID, errors.New("transaction requires a single manage_data operation") - } - op, ok := operations[0].(*ManageData) - if !ok { - return tx, clientAccountID, errors.New("operation type should be manage_data") - } - if op.SourceAccount == nil { - return tx, clientAccountID, errors.New("operation should have a source account") - } - clientAccountID = op.SourceAccount.GetAccountID() - rawOperations := tx.envelope.Operations() - if len(rawOperations) > 0 && rawOperations[0].SourceAccount.Type == xdr.CryptoKeyTypeKeyTypeMuxedEd25519 { - err = errors.New("invalid operation source account: only valid Ed25519 accounts are allowed in challenge transactions") - return tx, clientAccountID, err - } - - // verify manage data value - nonceB64 := string(op.Value) - if len(nonceB64) != 64 { - return tx, clientAccountID, errors.New("random nonce encoded as base64 should be 64 bytes long") - } - nonceBytes, err := base64.StdEncoding.DecodeString(nonceB64) - if err != nil { - return tx, clientAccountID, errors.Wrap(err, "failed to decode random nonce provided in manage_data operation") - } - if len(nonceBytes) != 48 { - return tx, clientAccountID, errors.New("random nonce before encoding as base64 should be 48 bytes long") - } - - err = verifyTxSignature(tx, network, serverAccountID) - if err != nil { - return tx, clientAccountID, err - } - - return tx, clientAccountID, nil -} - -// VerifyChallengeTxThreshold verifies that for a SEP 10 challenge transaction -// all signatures on the transaction are accounted for and that the signatures -// meet a threshold on an account. A transaction is verified if it is signed by -// the server account, and all other signatures match a signer that has been -// provided as an argument, and those signatures meet a threshold on the -// account. -// -// Signers that are not prefixed as an address/account ID strkey (G...) will be -// ignored. -// -// Errors will be raised if: -// - The transaction is invalid according to ReadChallengeTx. -// - No client signatures are found on the transaction. -// - One or more signatures in the transaction are not identifiable as the -// server account or one of the signers provided in the arguments. -// - The signatures are all valid but do not meet the threshold. -func VerifyChallengeTxThreshold(challengeTx, serverAccountID, network string, threshold Threshold, signerSummary SignerSummary) (signersFound []string, err error) { - signers := make([]string, 0, len(signerSummary)) - for s := range signerSummary { - signers = append(signers, s) - } - - signersFound, err = VerifyChallengeTxSigners(challengeTx, serverAccountID, network, signers...) - if err != nil { - return nil, err - } - - weight := int32(0) - for _, s := range signersFound { - weight += signerSummary[s] - } - - if weight < int32(threshold) { - return nil, errors.Errorf("signers with weight %d do not meet threshold %d", weight, threshold) - } - - return signersFound, nil -} - -// VerifyChallengeTxSigners verifies that for a SEP 10 challenge transaction -// all signatures on the transaction are accounted for. A transaction is -// verified if it is signed by the server account, and all other signatures -// match a signer that has been provided as an argument. Additional signers can -// be provided that do not have a signature, but all signatures must be matched -// to a signer for verification to succeed. If verification succeeds a list of -// signers that were found is returned, excluding the server account ID. -// -// Signers that are not prefixed as an address/account ID strkey (G...) will be -// ignored. -// -// Errors will be raised if: -// - The transaction is invalid according to ReadChallengeTx. -// - No client signatures are found on the transaction. -// - One or more signatures in the transaction are not identifiable as the -// server account or one of the signers provided in the arguments. -func VerifyChallengeTxSigners(challengeTx, serverAccountID, network string, signers ...string) ([]string, error) { - // Read the transaction which validates its structure. - tx, _, err := ReadChallengeTx(challengeTx, serverAccountID, network) - if err != nil { - return nil, err - } - - // Ensure the server account ID is an address and not a seed. - serverKP, err := keypair.ParseAddress(serverAccountID) - if err != nil { - return nil, err - } - - // Deduplicate the client signers and ensure the server is not included - // anywhere we check or output the list of signers. - clientSigners := []string{} - clientSignersSeen := map[string]struct{}{} - for _, signer := range signers { - // Ignore the server signer if it is in the signers list. It's - // important when verifying signers of a challenge transaction that we - // only verify and return client signers. If an account has the server - // as a signer the server should not play a part in the authentication - // of the client. - if signer == serverKP.Address() { - continue - } - // Deduplicate. - if _, seen := clientSignersSeen[signer]; seen { - continue - } - // Ignore non-G... account/address signers. - strkeyVersionByte, strkeyErr := strkey.Version(signer) - if strkeyErr != nil { - continue - } - if strkeyVersionByte != strkey.VersionByteAccountID { - continue - } - clientSigners = append(clientSigners, signer) - clientSignersSeen[signer] = struct{}{} - } - - // Don't continue if none of the signers provided are in the final list. - if len(clientSigners) == 0 { - return nil, errors.New("no verifiable signers provided, at least one G... address must be provided") - } - - // Verify all the transaction's signers (server and client) in one - // hit. We do this in one hit here even though the server signature was - // checked in the ReadChallengeTx to ensure that every signature and signer - // are consumed only once on the transaction. - allSigners := append([]string{serverKP.Address()}, clientSigners...) - allSignersFound, err := verifyTxSignatures(tx, network, allSigners...) - if err != nil { - return nil, err - } - - // Confirm the server is in the list of signers found and remove it. - serverSignerFound := false - signersFound := make([]string, 0, len(allSignersFound)-1) - for _, signer := range allSignersFound { - if signer == serverKP.Address() { - serverSignerFound = true - continue - } - signersFound = append(signersFound, signer) - } - - // Confirm we matched a signature to the server signer. - if !serverSignerFound { - return nil, errors.Errorf("transaction not signed by %s", serverKP.Address()) - } - - // Confirm we matched signatures to the client signers. - if len(signersFound) == 0 { - return nil, errors.Errorf("transaction not signed by %s", strings.Join(clientSigners, ", ")) - } - - // Confirm all signatures were consumed by a signer. - if len(allSignersFound) != len(tx.Signatures()) { - return signersFound, errors.Errorf("transaction has unrecognized signatures") - } - - return signersFound, nil -} - -// verifyTxSignature checks if a transaction has been signed by the provided Stellar account. -func verifyTxSignature(tx *Transaction, network string, signer string) error { - signersFound, err := verifyTxSignatures(tx, network, signer) - if len(signersFound) == 0 { - return errors.Errorf("transaction not signed by %s", signer) - } - return err -} - -// verifyTxSignature checks if a transaction has been signed by one or more of -// the signers, returning a list of signers that were found to have signed the -// transaction. -func verifyTxSignatures(tx *Transaction, network string, signers ...string) ([]string, error) { - txHash, err := tx.Hash(network) - if err != nil { - return nil, err - } - - // find and verify signatures - signatureUsed := map[int]bool{} - signersFound := map[string]struct{}{} - for _, signer := range signers { - kp, err := keypair.ParseAddress(signer) - if err != nil { - return nil, errors.Wrap(err, "signer not address") - } - - for i, decSig := range tx.Signatures() { - if signatureUsed[i] { - continue - } - if decSig.Hint != kp.Hint() { - continue - } - err := kp.Verify(txHash[:], decSig.Signature) - if err == nil { - signatureUsed[i] = true - signersFound[signer] = struct{}{} - break - } - } - } - - signersFoundList := make([]string, 0, len(signersFound)) - for _, signer := range signers { - if _, ok := signersFound[signer]; ok { - signersFoundList = append(signersFoundList, signer) - delete(signersFound, signer) - } - } - return signersFoundList, nil -} diff --git a/services/horizon/internal/txnbuild/transaction_fee_test.go b/services/horizon/internal/txnbuild/transaction_fee_test.go deleted file mode 100644 index 688ef8f3de..0000000000 --- a/services/horizon/internal/txnbuild/transaction_fee_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package txnbuild - -import ( - "math" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMinBaseFee(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee - 1, - Timebounds: NewInfiniteTimeout(), - }, - ) - - assert.EqualError(t, err, "base fee cannot be lower than network minimum of 100") -} - -func TestFeeBumpMinBaseFee(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - tx.baseFee -= 2 - - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: newKeypair1().Address(), - BaseFee: MinBaseFee - 1, - Inner: tx, - }, - ) - assert.EqualError(t, err, "base fee cannot be lower than network minimum of 100") - -} - -func TestFeeOverflow(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}, &Inflation{}}, - BaseFee: math.MaxUint32 / 2, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}, &Inflation{}, &Inflation{}}, - BaseFee: math.MaxUint32 / 2, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.EqualError(t, err, "base fee 2147483647 results in an overflow of max fee") -} - -func TestFeeBumpOverflow(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - convertToV1Tx(tx) - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: newKeypair1().Address(), - BaseFee: math.MaxInt64 / 2, - Inner: tx, - }, - ) - assert.NoError(t, err) - - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: newKeypair1().Address(), - BaseFee: math.MaxInt64, - Inner: tx, - }, - ) - assert.EqualError(t, err, "base fee 9223372036854775807 results in an overflow of max fee") -} - -func TestFeeBumpFeeGreaterThanOrEqualInner(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{&Inflation{}}, - BaseFee: 2 * MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - convertToV1Tx(tx) - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: newKeypair1().Address(), - BaseFee: 2 * MinBaseFee, - Inner: tx, - }, - ) - assert.NoError(t, err) - - _, err = NewFeeBumpTransaction( - FeeBumpTransactionParams{ - FeeAccount: newKeypair1().Address(), - BaseFee: 2*MinBaseFee - 1, - Inner: tx, - }, - ) - assert.EqualError(t, err, "base fee cannot be lower than provided inner transaction fee") -} diff --git a/services/horizon/internal/txnbuild/transaction_test.go b/services/horizon/internal/txnbuild/transaction_test.go deleted file mode 100644 index 97ae961dac..0000000000 --- a/services/horizon/internal/txnbuild/transaction_test.go +++ /dev/null @@ -1,2947 +0,0 @@ -package txnbuild - -import ( - "crypto/sha256" - "encoding/base64" - "strings" - "testing" - "time" - - "github.com/stellar/go/keypair" - "github.com/stellar/go/network" - "github.com/stellar/go/strkey" - "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMissingSourceAccount(t *testing.T) { - _, err := NewTransaction(TransactionParams{}) - assert.EqualError(t, err, "transaction has no source account") -} - -func TestIncrementSequenceNum(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 1) - inflation := Inflation{} - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&inflation}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - assert.Equal(t, int64(2), sourceAccount.Sequence) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&inflation}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - assert.Equal(t, int64(3), sourceAccount.Sequence) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&inflation}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - assert.Equal(t, int64(3), sourceAccount.Sequence) - - _, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: false, - Operations: []Operation{&inflation}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - assert.Equal(t, int64(3), sourceAccount.Sequence) -} - -func TestFeeNoOperations(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), 5938436531814403) - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - Operations: []Operation{}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.EqualError(t, err, "transaction has no operations") -} - -func TestInflation(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(3556091187167235)) - - inflation := Inflation{} - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&inflation}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - // https://www.stellar.org/laboratory/#xdr-viewer?input=AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAHqLnLFAAAAQP3NHWXvzKIHB3%2BjjhHITdc%2FtBPntWYj3SoTjpON%2BdxjKqU5ohFamSHeqi5ONXkhE9Uajr5sVZXjQfUcTTzsWAA%3D&type=TransactionEnvelope - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAHqLnLFAAAAQP3NHWXvzKIHB3+jjhHITdc/tBPntWYj3SoTjpON+dxjKqU5ohFamSHeqi5ONXkhE9Uajr5sVZXjQfUcTTzsWAA=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestCreateAccount(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABAHsyMojA0Q5MiNsR5X5AiNpCn9mlXmqluRsNpTniCR91M4U5TFmrrqVNLkU58/l+Y8hUPwidDTRSzLZKbMUL/Bw==" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestPayment(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - payment := Payment{ - Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", - Amount: "10", - Asset: NativeAsset{}, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&payment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAF9eEAAAAAAAAAAAHqLnLFAAAAQNcGQpjNOFCLf9eEmobN+H8SNoDH/jMrfEFPX8kM212ST+TGfirEdXH77GJXvaWplfGKmE3B+UDwLuYLwO+KbQQ=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestPaymentFailsIfNoAssetSpecified(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639898)) - - payment := Payment{ - Destination: "GB7BDSZU2Y27LYNLALKKALB52WS2IZWYBDGY6EQBLEED3TJOCVMZRH7H", - Amount: "10", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&payment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - expectedErrMsg := "validation failed for *txnbuild.Payment operation: Field: Asset, Error: asset is undefined" - require.EqualError(t, err, expectedErrMsg, "An asset is required") -} - -func TestBumpSequence(t *testing.T) { - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - bumpSequence := BumpSequence{ - BumpTo: 9606132444168300, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&bumpSequence}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAiILoAAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAALACIgugAAAGwAAAAAAAAAAdKHZH4AAABAndjSSWeACpbr0ROAEK6jw5CzHiL/rCDpa6AO05+raHDowSUJBckkwlEuCjbBoO/A06tZNRT1Per3liTQrc8fCg==" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestAccountMerge(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484298)) - - accountMerge := AccountMerge{ - Destination: "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP", - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&accountMerge}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAALAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAIAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAAAAAHqLnLFAAAAQJ/UcOgE64+GQpwv0uXXa2jrKtFdmDsyZ6ZZ/udxryPS8cNCm2L784ixPYM4XRgkoQCdxC3YK8n5x5+CXLzrrwA=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageData(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(3556091187167235)) - - manageData := ManageData{ - Name: "Fruit preference", - Value: []byte("Apple"), - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&manageData}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - // https://www.stellar.org/laboratory/#txsigner?xdr=AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAABAAAABUFwcGxlAAAAAAAAAAAAAAA%3D - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAABAAAABUFwcGxlAAAAAAAAAAAAAAHqLnLFAAAAQO1ELJBEoqBDyIsS7uSJwe1LOimV/E+09MyF1G/+yrxSggFVPEjD5LXcm/6POze3IsMuIYJU1et5Q2Vt9f73zQo=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageDataRemoveDataEntry(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484309)) - - manageData := ManageData{ - Name: "Fruit preference", - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&manageData}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAWAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAEEZydWl0IHByZWZlcmVuY2UAAAAAAAAAAAAAAAHqLnLFAAAAQB8rkFZgtffUTdCASzwJ3jRcMCzHpVbbuFbye7Ki2dLao6u5d2aSzz3M2ugNJjNFMfSu3io9adCqwVKKjk0UJQA=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsInflationDestination(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484315)) - - setOptions := SetOptions{ - InflationDestination: NewInflationDestination(kp1.Address()), - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAcAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAQAAAAAlyvHaD8duz+iEXkJUUbsHkklIlH46oMrMMYrt0odkfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHqLnLFAAAAQB0RLe9DjdHzLM22whFja3ZT97L/818lvWpk5EOTETr9lmDH7/A0/EAzeCkTBzZMCi3C6pV1PrGBr0NJdRrPowg=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsSetFlags(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484318)) - - setOptions := SetOptions{ - SetFlags: []AccountFlag{AuthRequired, AuthRevocable}, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAfAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6i5yxQAAAECfYTppxtp1A2zSbb6VzkOkyk9D/7xjaXRxR+ZIqgdK3lWkHQRkjyVBj2yaI61J3trdp7CswImptjkjLprt0WIO" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsClearFlags(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484319)) - - setOptions := SetOptions{ - ClearFlags: []AccountFlag{AuthRequired, AuthRevocable}, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAgAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6i5yxQAAAEANXPAN+RgvqjGF0kJ6MyNTiMnWaELw5vYNwxhv8+mi3KmGWMzojCxcmMAqni0zBMsEjl9z7H8JT9x05OlQ9nsD" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsMasterWeight(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484320)) - - setOptions := SetOptions{ - MasterWeight: NewThreshold(10), - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAhAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAQAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6i5yxQAAAECIxH2W4XZ5fMsG658hdIEys2nlVSAK1FEjT5GADF6sWEThGFc+Wrmlw6GwKn6ZNAmxVULEgircjQx48aYSgFYD" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsThresholds(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484322)) - - setOptions := SetOptions{ - LowThreshold: NewThreshold(1), - MediumThreshold: NewThreshold(2), - HighThreshold: NewThreshold(2), - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAjAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAIAAAABAAAAAgAAAAAAAAAAAAAAAAAAAAHqLnLFAAAAQFwRcFbzEtxoxZOtWlOQld3nURHZugNj5faEncpv0X/dcrfiQVU7k3fkTYDskiVExFiq78CBsYAr0uuvfH61IQs=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsHomeDomain(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484325)) - - setOptions := SetOptions{ - HomeDomain: NewHomeDomain("LovelyLumensLookLuminous.com"), - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAmAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAcTG92ZWx5THVtZW5zTG9va0x1bWlub3VzLmNvbQAAAAAAAAAAAAAAAeoucsUAAABAtC4HZzvRfyphRg5jjmz5jzBn86SANXCZS59GejRE8L1uCOxgXSEVoh1b+UetUEi7JN/n1ECBEVJrXgj0c34eBg==" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestSetOptionsHomeDomainTooLong(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484323)) - - setOptions := SetOptions{ - HomeDomain: NewHomeDomain("LovelyLumensLookLuminousLately.com"), - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - - assert.Error(t, err, "A validation error was expected (home domain > 32 chars)") -} - -func TestSetOptionsSigner(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484325)) - - setOptions := SetOptions{ - Signer: &Signer{Address: kp1.Address(), Weight: Threshold(4)}, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAAmAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAABAAAAAAAAAAB6i5yxQAAAEBfgmUK+wNj8ROz78Sg0rQ2s7lmtvA4r5epHkqc9yoxLDr/GSkmgWneVqoKNxWF0JB9L+Gql1+f8M8p1McF4MsB" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestMultipleOperations(t *testing.T) { - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(9606132444168199)) - - inflation := Inflation{} - bumpSequence := BumpSequence{ - BumpTo: 9606132444168300, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&inflation, &bumpSequence}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAyAAiILoAAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAAsAIiC6AAAAbAAAAAAAAAAB0odkfgAAAEDmf3Ag2Hw5NdlvzJpph4Km+aNKy8kfzS1EAhIVdKJwUnMVWhOpfdXSh/aekEVdoxXh2+ioocrxdtkWAZfS3sMF" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestChangeTrust(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484348)) - - changeTrust := ChangeTrust{ - Line: CreditAsset{"ABCD", kp1.Address()}, - Limit: "10", - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&changeTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAAA9AAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAABfXhAAAAAAAAAAAB6i5yxQAAAED7YSd1VdewEdtEURAYuyCy8dWbzALEf1vJn88/gCER4CNdIvojOEafJEhYhzZJhdG7oa+95UjfI9vMJO8qdWMK" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestChangeTrustNativeAssetNotAllowed(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484348)) - - changeTrust := ChangeTrust{ - Line: NativeAsset{}, - Limit: "10", - } - - _, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&changeTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - - expectedErrMsg := "validation failed for *txnbuild.ChangeTrust operation: Field: Line, Error: native (XLM) asset type is not allowed" - require.EqualError(t, err, expectedErrMsg, "No trustlines for native assets") -} - -func TestChangeTrustDeleteTrustline(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484354)) - - issuedAsset := CreditAsset{"ABCD", kp1.Address()} - removeTrust := RemoveTrustlineOp(issuedAsset) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&removeTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAABDAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAUFCQ0QAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAAAAAAAAAAAAAAAAAB6i5yxQAAAECgd2wkK35civvf6NKpsSFDyKpdyo/cs7wL+RYfZ2BCP7eGrUUpu2GfQFtf/Hm6aBwT6nJ+dONTSPXnyp7Dq18L" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestAllowTrust(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484366)) - - issuedAsset := CreditAsset{"ABCD", kp1.Address()} - allowTrust := AllowTrust{ - Trustor: kp1.Address(), - Type: issuedAsset, - Authorize: true, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&allowTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAABPAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAHAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAUFCQ0QAAAABAAAAAAAAAAHqLnLFAAAAQGGBSKitYxpHNMaVVOE2CIylWFJgwqxjhwnIvWauSSkLapntD18G1pMahLbs8Lqcr3+cEs5WjLI4eBhy6WiJhAk=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestAllowTrustNoIssuer(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(40385577484366)) - - issuedAsset := CreditAsset{Code: "XYZ"} - allowTrust := AllowTrust{ - Trustor: kp1.Address(), - Type: issuedAsset, - Authorize: true, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&allowTrust}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAAJLsAAABPAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAHAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAVhZWgAAAAABAAAAAAAAAAHqLnLFAAAAQO8mcsi/+RObrKto8tABtN8RwUi6101FqBDTwqMQp4hNuujw+SGEFaBCYLNw/u40DHFRQoBNi6zcBKbBSg+gVwE=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageSellOfferNewOffer(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", kp0.Address()} - sellAmount := "100" - price := "0.01" - createOffer, err := CreateOfferOp(selling, buying, sellAmount, price) - check(err) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAAFAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAAHSh2R+AAAAQAmXf4BnH8bWhy+Tnxf+7zgsij7pV0b7XC4rqfYWi9ZIVUaidWPbrFhaWjiQbXYB1NKdx0XjidzkcAgMInLqDgs=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageSellOfferDeleteOffer(t *testing.T) { - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761105)) - - offerID := int64(2921622) - deleteOffer, err := DeleteOfferOp(offerID) - check(err) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&deleteOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAASAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFGQUtFAAAAAEEHgGTElYZi82AkGiJdSja2OBaU2aEcwwp3AY3tFJ2xAAAAAAAAAAAAAAABAAAAAQAAAAAALJSWAAAAAAAAAAHSh2R+AAAAQBSjRfpyEAIMnRQOPf1BBOx8HFC6Lm6bxxdljaegnUts8SmWJGQbZN5a8PQGzOTwGdBKBk9X9d+BIrBVc3kyyQ4=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageSellOfferUpdateOffer(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761097)) - - selling := NativeAsset{} - buying := CreditAsset{"ABCD", kp0.Address()} - sellAmount := "50" - price := "0.02" - offerID := int64(2497628) - updateOffer, err := UpdateOfferOp(selling, buying, sellAmount, price, offerID) - check(err) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&updateOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAAKAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAB3NZQAAAAABAAAAMgAAAAAAJhxcAAAAAAAAAAHSh2R+AAAAQAwqWg2C/oe/zH4D3Y7/yg5SlHqFvF6A3j6GQZ9NPh3ROqutovLyAE62+rvXxM7hqSNz1Rtx4frJaOhOabh6DAg=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestCreatePassiveSellOffer(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761100)) - - createPassiveOffer := CreatePassiveSellOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", kp0.Address()}, - Amount: "10", - Price: "1.0", - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createPassiveOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAANAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAX14QAAAAABAAAAAQAAAAAAAAAB0odkfgAAAEAgUD7M1UL7x2m2m26ySzcSHxIneOT7/r+s/HLsgWDj6CmpSi1GZrlvtBH+CNuegCwvW09TRZJhp7bLywkaFCoK" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestPathPayment(t *testing.T) { - kp0 := newKeypair0() - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(187316408680450)) - - abcdAsset := CreditAsset{"ABCD", kp0.Address()} - pathPayment := PathPayment{ - SendAsset: NativeAsset{}, - SendMax: "10", - Destination: kp2.Address(), - DestAsset: NativeAsset{}, - DestAmount: "1", - Path: []Asset{abcdAsset}, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&pathPayment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp2, - ) - assert.NoError(t, err) - - expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAAql0AAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAF9eEAAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAAAmJaAAAAAAQAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAAAAAAAEuFVmYAAAAQF2kLUL/RoFIy1cmt+GXdWn2tDUjJYV3YwF4A82zIBhqYSO6ogOoLPNRt3w+IGCAgfR4Q9lpax+wCXWoQERHSw4=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestMemoText(t *testing.T) { - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(3556099777101824)) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&BumpSequence{BumpTo: 1}}, - Memo: MemoText("Twas brillig"), - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp2, - ) - assert.NoError(t, err) - - // https://www.stellar.org/laboratory/#txsigner?xdr=AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMokEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAADFR3YXMgYnJpbGxpZwAAAAEAAAAAAAAACwAAAAAAAAABAAAAAAAAAAA%3D&network=test - expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMokEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAADFR3YXMgYnJpbGxpZwAAAAEAAAAAAAAACwAAAAAAAAABAAAAAAAAAAEuFVmYAAAAQILT8/7MGTmWkfjMi6Y23n2cVWs+IMY67xOskTivSZehp7wWaDXLIdCbdijmG64+Nz+fPBT9HYMqSRDcLiZYDQ0=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestMemoID(t *testing.T) { - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(3428320205078528)) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&BumpSequence{BumpTo: 1}}, - Memo: MemoID(314159), - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp2, - ) - assert.NoError(t, err) - - expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMLgoAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAEyy8AAAABAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAABLhVZmAAAAEA5P/V/Veh6pjXj7CnqtWDATh8II+ci1z3/zmNk374XLuVLzx7jRve59AKnPMwIPwDJ8cXwEKz8+fYOIkfEI9AJ" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestMemoHash(t *testing.T) { - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(3428320205078528)) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&BumpSequence{BumpTo: 1}}, - Memo: MemoHash([32]byte{0x01}), - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp2, - ) - assert.NoError(t, err) - - expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMLgoAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAABLhVZmAAAAEAgauaUpqEGF1VeXYtkYg0I19QC3GJVrCPOqDHPIdXvGkQ9N+3Vt6yfKIN0sE/X5NuD6FhArQ3adwvZeaNDilwN" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestMemoReturn(t *testing.T) { - kp2 := newKeypair2() - sourceAccount := NewSimpleAccount(kp2.Address(), int64(3428320205078528)) - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&BumpSequence{BumpTo: 1}}, - Memo: MemoReturn([32]byte{0x01}), - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp2, - ) - assert.NoError(t, err) - - expected := "AAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAZAAMLgoAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAABLhVZmAAAAEAuLFTunY08pbWKompoepHdazLmr7uePUSOzA4P33+SVRKWiu+h2tngOsP8hga+wpLJXT9l/0uMQ3iziRVUrh0K" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageBuyOfferNewOffer(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761092)) - - buyOffer := ManageBuyOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", kp0.Address()}, - Amount: "100", - Price: "0.01", - OfferID: 0, - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - // https://www.stellar.org/laboratory/#xdr-viewer?input=AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R%2BAAAAZAAAJWoAAAAFAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAAHSh2R%2BAAAAQHwuorW7BvBwJAz%2BETSteeDZ9UKhox1y1BqJLvaIkWSr5rNbOpimjWQxrUNQoy%2B%2BwmtY8tiMSv3Jbz8Dd4QTaQU%3D&type=TransactionEnvelope&network=test - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAAFAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQkNEAAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAADuaygAAAAABAAAAZAAAAAAAAAAAAAAAAAAAAAHSh2R+AAAAQHwuorW7BvBwJAz+ETSteeDZ9UKhox1y1BqJLvaIkWSr5rNbOpimjWQxrUNQoy++wmtY8tiMSv3Jbz8Dd4QTaQU=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageBuyOfferDeleteOffer(t *testing.T) { - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761105)) - - buyOffer := ManageBuyOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", kp1.Address()}, - Amount: "0", - Price: "0.01", - OfferID: int64(2921622), - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - // https://www.stellar.org/laboratory/#xdr-viewer?input=AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R%2BAAAAZAAAJWoAAAASAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R%2BAAAAAAAAAAAAAAABAAAAZAAAAAAALJSWAAAAAAAAAAHSh2R%2BAAAAQItno%2BcpmUYFvxLcYVaDonTV3dmvzz%2B2SLzKRrYoXOqK8wCZjcP%2FkgzPMmXhTtF2tgQ9qb0rAIYpH9%2FrjtZPBgY%3D&type=TransactionEnvelope&network=test - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAASAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAAAAAAAAAABAAAAZAAAAAAALJSWAAAAAAAAAAHSh2R+AAAAQItno+cpmUYFvxLcYVaDonTV3dmvzz+2SLzKRrYoXOqK8wCZjcP/kgzPMmXhTtF2tgQ9qb0rAIYpH9/rjtZPBgY=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestManageBuyOfferUpdateOffer(t *testing.T) { - kp1 := newKeypair1() - sourceAccount := NewSimpleAccount(kp1.Address(), int64(41137196761097)) - - buyOffer := ManageBuyOffer{ - Selling: NativeAsset{}, - Buying: CreditAsset{"ABCD", kp1.Address()}, - Amount: "50", - Price: "0.02", - OfferID: int64(2921622), - } - - received, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&buyOffer}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp1, - ) - assert.NoError(t, err) - - // https://www.stellar.org/laboratory/#xdr-viewer?input=AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R%2BAAAAZAAAJWoAAAAKAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R%2BAAAAAB3NZQAAAAABAAAAMgAAAAAALJSWAAAAAAAAAAHSh2R%2BAAAAQK%2FsasTxgNqvkz3dGaDOyUgfa9UAAmUBmgiyaQU1dMlNNvTVH1D7PQKXkTooWmb6qK7Ee8vaTCFU6gGmShhA9wE%3D&type=TransactionEnvelope&network=test - expected := "AAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAZAAAJWoAAAAKAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAB3NZQAAAAABAAAAMgAAAAAALJSWAAAAAAAAAAHSh2R+AAAAQK/sasTxgNqvkz3dGaDOyUgfa9UAAmUBmgiyaQU1dMlNNvTVH1D7PQKXkTooWmb6qK7Ee8vaTCFU6gGmShhA9wE=" - assert.Equal(t, expected, received, "Base 64 XDR should match") -} - -func TestBuildChallengeTx(t *testing.T) { - kp0 := newKeypair0() - - { - // 1 minute timebound - tx, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "SDF", network.TestNetworkPassphrase, time.Minute) - assert.NoError(t, err) - txeBase64, err := tx.Base64() - assert.NoError(t, err) - var txXDR xdr.TransactionEnvelope - err = xdr.SafeUnmarshalBase64(txeBase64, &txXDR) - assert.NoError(t, err) - assert.Equal(t, int64(0), txXDR.SeqNum(), "sequence number should be 0") - assert.Equal(t, uint32(100), txXDR.Fee(), "Fee should be 100") - assert.Equal(t, 1, len(txXDR.Operations()), "number operations should be 1") - timeDiff := txXDR.TimeBounds().MaxTime - txXDR.TimeBounds().MinTime - assert.Equal(t, int64(60), int64(timeDiff), "time difference should be 300 seconds") - op := txXDR.Operations()[0] - assert.Equal(t, xdr.OperationTypeManageData, op.Body.Type, "operation type should be manage data") - assert.Equal(t, xdr.String64("SDF auth"), op.Body.ManageDataOp.DataName, "DataName should be 'SDF auth'") - assert.Equal(t, 64, len(*op.Body.ManageDataOp.DataValue), "DataValue should be 64 bytes") - - } - - { - // 5 minutes timebound - tx, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "SDF1", network.TestNetworkPassphrase, time.Duration(5*time.Minute)) - assert.NoError(t, err) - txeBase64, err := tx.Base64() - assert.NoError(t, err) - var txXDR1 xdr.TransactionEnvelope - err = xdr.SafeUnmarshalBase64(txeBase64, &txXDR1) - assert.NoError(t, err) - assert.Equal(t, int64(0), txXDR1.SeqNum(), "sequence number should be 0") - assert.Equal(t, uint32(100), txXDR1.Fee(), "Fee should be 100") - assert.Equal(t, 1, len(txXDR1.Operations()), "number operations should be 1") - - timeDiff := txXDR1.TimeBounds().MaxTime - txXDR1.TimeBounds().MinTime - assert.Equal(t, int64(300), int64(timeDiff), "time difference should be 300 seconds") - op1 := txXDR1.Operations()[0] - assert.Equal(t, xdr.OperationTypeManageData, op1.Body.Type, "operation type should be manage data") - assert.Equal(t, xdr.String64("SDF1 auth"), op1.Body.ManageDataOp.DataName, "DataName should be 'SDF1 auth'") - assert.Equal(t, 64, len(*op1.Body.ManageDataOp.DataValue), "DataValue should be 64 bytes") - } - - //transaction with infinite timebound - _, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "sdf", network.TestNetworkPassphrase, 0) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "provided timebound must be at least 1s (300s is recommended)") - } -} - -func TestHashHex(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - - txeB64, err := tx.Base64() - assert.NoError(t, err) - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABAHsyMojA0Q5MiNsR5X5AiNpCn9mlXmqluRsNpTniCR91M4U5TFmrrqVNLkU58/l+Y8hUPwidDTRSzLZKbMUL/Bw==" - assert.Equal(t, expected, txeB64, "Base 64 XDR should match") - - hashHex, err := tx.HashHex(network.TestNetworkPassphrase) - assert.NoError(t, err) - expected = "1b3905ba8c3c0ecc68ae812f2d77f27c697195e8daf568740fc0f5662f65f759" - assert.Equal(t, expected, hashHex, "hex encoded hash should match") - - txEnv, err := tx.TxEnvelope() - assert.NoError(t, err) - assert.NotNil(t, txEnv, "transaction xdr envelope should not be nil") - sourceAccountFromEnv := txEnv.SourceAccount().ToAccountId() - assert.Equal(t, sourceAccount.AccountID, sourceAccountFromEnv.Address()) - assert.Equal(t, uint32(100), txEnv.Fee()) - assert.Equal(t, sourceAccount.Sequence, int64(txEnv.SeqNum())) - assert.Equal(t, xdr.MemoTypeMemoNone, txEnv.Memo().Type) - assert.Len(t, txEnv.Operations(), 1) -} - -func TestTransactionFee(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - assert.Equal(t, int64(100), tx.BaseFee(), "Transaction base fee should match") - assert.Equal(t, int64(100), tx.MaxFee(), "Transaction fee should match") - - tx, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: 500, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - assert.Equal(t, int64(500), tx.BaseFee(), "Transaction base fee should match") - assert.Equal(t, int64(500), tx.MaxFee(), "Transaction fee should match") - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - - txeB64, err := tx.Base64() - assert.NoError(t, err) - expected := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAB9AAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABAnc69aKw6dg1LlHxkIetKZu8Ou8hgbj4mICV0tiOJeuiq8DvivSlAngnD+FlVIaotmg8i3dEzBg+LcLnG9UttBQ==" - assert.Equal(t, expected, txeB64, "Base 64 XDR should match") -} - -func TestPreAuthTransaction(t *testing.T) { - // Address: GDK3YEHGI3ORGVO7ZEV2XF4SV5JU3BOKHMHPP4QFJ74ZRIIRROZ7ITOJ - kp0 := newKeypair("SDY4PF6F6OWWERZT6OL2LVNREHUGHKALUI5W4U2JK4GAKPAC2RM43OAU") - sourceAccount := NewSimpleAccount(kp0.Address(), int64(4353383146192898)) // sequence number is in the future - - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - } - - // build transaction to be submitted in the future. - txFuture, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - // save the hash of the future transaction. - txFutureHash, err := txFuture.Hash(network.TestNetworkPassphrase) - assert.NoError(t, err) - - // sign transaction without keypairs, the hash of the future transaction on the account - // will be used for authorisation. - txFuture, err = txFuture.Sign(network.TestNetworkPassphrase) - assert.NoError(t, err) - - txeFutureB64, err := txFuture.Base64() - assert.NoError(t, err) - expected := "AAAAANW8EOZG3RNV38krq5eSr1NNhco7DvfyBU/5mKERi7P0AAAAZAAPd2EAAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAA==" - assert.Equal(t, expected, txeFutureB64, "Base 64 XDR should match") - - //encode the txFutureHash as a stellar HashTx signer key. - preAuth, err := strkey.Encode(strkey.VersionByteHashTx, txFutureHash[:]) - assert.NoError(t, err) - - // set sequence number to the current number. - sourceAccount.Sequence = int64(4353383146192897) - - // add hash of future transaction as signer to account - setOptions := SetOptions{ - Signer: &Signer{Address: preAuth, Weight: Threshold(2)}, - } - - // build a transaction to add the hash of the future transaction as a signer on the account. - txNow, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: 500, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - assert.Equal(t, int64(500), txNow.MaxFee(), "Transaction fee should match") - txNow, err = txNow.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - - txeNowB64, err := txNow.Base64() - assert.NoError(t, err) - expected = "AAAAANW8EOZG3RNV38krq5eSr1NNhco7DvfyBU/5mKERi7P0AAAB9AAPd2EAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAY9c66YpCPn8yMopKaNBd7gbiD2cr+aTLOaZE4whmeO1AAAAAgAAAAAAAAABEYuz9AAAAEC62tXQKDTcrB8VvOQIaI3ECV0uypBkpGNuyodnLLY27ii4QMdB4g4otYIvKY6nbWQqYYapNh6Q9dVsYfK6OHQM" - assert.Equal(t, expected, txeNowB64, "Base 64 XDR should match") - // Note: txeFutureB64 can be submitted to the network after txeNowB64 has been applied to the account -} - -func TestHashXTransaction(t *testing.T) { - // 256 bit preimage - preimage := "this is a preimage for hashx transactions on the stellar network" - - preimageHash := sha256.Sum256([]byte(preimage)) - - //encode preimageHash as a stellar HashX signer key - hashx, err := strkey.Encode(strkey.VersionByteHashX, preimageHash[:]) - assert.NoError(t, err) - - // add hashx as signer to the account - setOptions := SetOptions{ - Signer: &Signer{Address: hashx, Weight: Threshold(1)}, - } - - // Address: GDK3YEHGI3ORGVO7ZEV2XF4SV5JU3BOKHMHPP4QFJ74ZRIIRROZ7ITOJ - kp0 := newKeypair("SDY4PF6F6OWWERZT6OL2LVNREHUGHKALUI5W4U2JK4GAKPAC2RM43OAU") - sourceAccount := NewSimpleAccount(kp0.Address(), int64(4353383146192899)) - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&setOptions}, - BaseFee: 500, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - assert.Equal(t, int64(500), tx.MaxFee(), "Transaction fee should match") - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - - txeB64, err := tx.Base64() - assert.NoError(t, err) - - expected := "AAAAANW8EOZG3RNV38krq5eSr1NNhco7DvfyBU/5mKERi7P0AAAB9AAPd2EAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAvslgb5oIfuISFP8FYvTqsciG1iSUerJB3Au6T2WLqMFAAAAAQAAAAAAAAABEYuz9AAAAECHBwfCbcOwFyoILLW7OZejvdbsVPEwB6z6ocAG4cRGu69vXKCrBFYD2mMdJRCeglgJgfgaFj2qshBgL8yQ14UH" - assert.Equal(t, expected, txeB64, "Base 64 XDR should match") - - // build a transaction to test hashx signer - payment := Payment{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - Asset: NativeAsset{}, - } - - sourceAccount.Sequence = int64(4353383146192902) - - tx, err = NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&payment}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - // sign transaction with the preimage - tx, err = tx.SignHashX([]byte(preimage)) - assert.NoError(t, err) - - txeB64, err = tx.Base64() - assert.NoError(t, err) - expected = "AAAAANW8EOZG3RNV38krq5eSr1NNhco7DvfyBU/5mKERi7P0AAAAZAAPd2EAAAAHAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAAAAAAF9eEAAAAAAAAAAAGWLqMFAAAAQHRoaXMgaXMgYSBwcmVpbWFnZSBmb3IgaGFzaHggdHJhbnNhY3Rpb25zIG9uIHRoZSBzdGVsbGFyIG5ldHdvcms=" - assert.Equal(t, expected, txeB64, "Base 64 XDR should match") - -} - -func TestFromXDR(t *testing.T) { - txeB64 := "AAAAACYWIvM98KlTMs0IlQBZ06WkYpZ+gILsQN6ega0++I/sAAAAZAAXeEkAAAABAAAAAAAAAAEAAAAQMkExVjZKNTcwM0c0N1hIWQAAAAEAAAABAAAAACYWIvM98KlTMs0IlQBZ06WkYpZ+gILsQN6ega0++I/sAAAAAQAAAADMSEvcRKXsaUNna++Hy7gWm/CfqTjEA7xoGypfrFGUHAAAAAAAAAACCPHRAAAAAAAAAAABPviP7AAAAEBu6BCKf4WZHPum5+29Nxf6SsJNN8bgjp1+e1uNBaHjRg3rdFZYgUqEqbHxVEs7eze3IeRbjMZxS3zPf/xwJCEI" - - tx, err := TransactionFromXDR(txeB64) - assert.NoError(t, err) - newTx, ok := tx.Transaction() - assert.True(t, ok) - _, ok = tx.FeeBump() - assert.False(t, ok) - - assert.Equal(t, "GATBMIXTHXYKSUZSZUEJKACZ2OS2IYUWP2AIF3CA32PIDLJ67CH6Y5UY", newTx.SourceAccount().AccountID, "source accounts should match") - assert.Equal(t, int64(100), newTx.BaseFee(), "Base fee should match") - sa := newTx.SourceAccount() - assert.Equal(t, int64(6606179392290817), sa.Sequence, "Sequence number should match") - assert.Equal(t, 1, len(newTx.Operations()), "Operations length should match") - assert.IsType(t, newTx.Operations()[0], &Payment{}, "Operation types should match") - paymentOp, ok1 := newTx.Operations()[0].(*Payment) - assert.Equal(t, true, ok1) - assert.Equal(t, "GATBMIXTHXYKSUZSZUEJKACZ2OS2IYUWP2AIF3CA32PIDLJ67CH6Y5UY", paymentOp.SourceAccount.GetAccountID(), "Operation source should match") - assert.Equal(t, "GDGEQS64ISS6Y2KDM5V67B6LXALJX4E7VE4MIA54NANSUX5MKGKBZM5G", paymentOp.Destination, "Operation destination should match") - assert.Equal(t, "874.0000000", paymentOp.Amount, "Operation amount should match") - - txeB64 = "AAAAAGigiN2q4qBXAERImNEncpaADylyBRtzdqpEsku6CN0xAAABkAAADXYAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAABm5ldyB0eAAAAAAAAgAAAAEAAAAA+Q2efEMLNGF4i+aYfutUXGMSlf8tNevKeS1Jl/oCVGkAAAAGAAAAAVVTRAAAAAAAaKCI3arioFcAREiY0SdyloAPKXIFG3N2qkSyS7oI3TF//////////wAAAAAAAAAKAAAABHRlc3QAAAABAAAABXZhbHVlAAAAAAAAAAAAAAA=" - - tx2, err := TransactionFromXDR(txeB64) - assert.NoError(t, err) - newTx2, ok := tx2.Transaction() - assert.True(t, ok) - _, ok = tx2.FeeBump() - assert.False(t, ok) - - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", newTx2.SourceAccount().AccountID, "source accounts should match") - assert.Equal(t, int64(200), newTx2.BaseFee(), "Base fee should match") - assert.Equal(t, int64(14800457302017), newTx2.SourceAccount().Sequence, "Sequence number should match") - - memo, ok := newTx2.Memo().(MemoText) - assert.Equal(t, true, ok) - assert.Equal(t, MemoText("new tx"), memo, "memo should match") - assert.Equal(t, 2, len(newTx2.Operations()), "Operations length should match") - assert.IsType(t, newTx2.Operations()[0], &ChangeTrust{}, "Operation types should match") - assert.IsType(t, newTx2.Operations()[1], &ManageData{}, "Operation types should match") - op1, ok1 := newTx2.Operations()[0].(*ChangeTrust) - assert.Equal(t, true, ok1) - assert.Equal(t, "GD4Q3HT4IMFTIYLYRPTJQ7XLKROGGEUV74WTL26KPEWUTF72AJKGSJS7", op1.SourceAccount.GetAccountID(), "Operation source should match") - assetType, err := op1.Line.GetType() - assert.NoError(t, err) - - assert.Equal(t, AssetTypeCreditAlphanum4, assetType, "Asset type should match") - assert.Equal(t, "USD", op1.Line.GetCode(), "Asset code should match") - assert.Equal(t, "GBUKBCG5VLRKAVYAIREJRUJHOKLIADZJOICRW43WVJCLES52BDOTCQZU", op1.Line.GetIssuer(), "Asset issuer should match") - assert.Equal(t, "922337203685.4775807", op1.Limit, "trustline limit should match") - - op2, ok2 := newTx2.Operations()[1].(*ManageData) - assert.Equal(t, true, ok2) - assert.Equal(t, nil, op2.SourceAccount, "Operation source should match") - assert.Equal(t, "test", op2.Name, "Name should match") - assert.Equal(t, "value", string(op2.Value), "Value should match") -} - -func TestBuild(t *testing.T) { - kp0 := newKeypair0() - sourceAccount := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &sourceAccount, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - expectedUnsigned := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAA==" - - expectedSigned := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABAHsyMojA0Q5MiNsR5X5AiNpCn9mlXmqluRsNpTniCR91M4U5TFmrrqVNLkU58/l+Y8hUPwidDTRSzLZKbMUL/Bw==" - - txeB64, err := tx.Base64() - assert.NoError(t, err) - assert.Equal(t, expectedUnsigned, txeB64, "tx envelope should match") - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - txeB64, err = tx.Base64() - assert.NoError(t, err) - assert.Equal(t, expectedSigned, txeB64, "tx envelope should match") -} - -func TestFromXDRBuildSignEncode(t *testing.T) { - expectedUnsigned := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAA==" - - expectedSigned := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABAHsyMojA0Q5MiNsR5X5AiNpCn9mlXmqluRsNpTniCR91M4U5TFmrrqVNLkU58/l+Y8hUPwidDTRSzLZKbMUL/Bw==" - - kp0 := newKeypair0() - - // test signing transaction without modification - tx, err := TransactionFromXDR(expectedUnsigned) - assert.NoError(t, err) - newTx, ok := tx.Transaction() - assert.True(t, ok) - _, ok = tx.FeeBump() - assert.False(t, ok) - - //passphrase is needed for signing - newTx, err = newTx.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - txeB64, err := newTx.Base64() - assert.NoError(t, err) - assert.Equal(t, expectedSigned, txeB64, "tx envelope should match") - - // test signing transaction with modification - expectedSigned2 := "AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAiII0AAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAABW5ld3R4AAAAAAAAAQAAAAAAAAAAAAAAAITg3tq8G0kvnvoIhZPMYJsY+9KVV8xAA6NxhtKxIXZUAAAAAAX14QAAAAAAAAAAAeoucsUAAABADPbbXNzpC408WyYGQszN3VA9e41sNpsyZ2HcS62RXvUDsN0A+IXMPRMaCb+Wgn1OM6Ikam9ol0MJYNeK0BPxCg==" - tx, err = TransactionFromXDR(expectedUnsigned) - assert.NoError(t, err) - newTx, ok = tx.Transaction() - assert.True(t, ok) - _, ok = tx.FeeBump() - assert.False(t, ok) - - txeB64, err = newSignedTransaction( - TransactionParams{ - SourceAccount: &SimpleAccount{ - AccountID: newTx.SourceAccount().AccountID, - Sequence: newTx.SourceAccount().Sequence + 1, - }, - IncrementSequenceNum: false, - Operations: newTx.Operations(), - BaseFee: newTx.BaseFee(), - Memo: MemoText("newtx"), - Timebounds: newTx.Timebounds(), - }, - network.TestNetworkPassphrase, - kp0, - ) - assert.NoError(t, err) - assert.Equal(t, expectedSigned2, txeB64, "tx envelope should match") -} - -func TestSignWithSecretKey(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - txSource := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - tx1Source := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - opSource := NewSimpleAccount(kp1.Address(), 0) - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - SourceAccount: &opSource, - } - - expected, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - tx1, err := NewTransaction( - TransactionParams{ - SourceAccount: &tx1Source, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - tx1, err = tx1.SignWithKeyString( - network.TestNetworkPassphrase, - "SBPQUZ6G4FZNWFHKUWC5BEYWF6R52E3SEP7R3GWYSM2XTKGF5LNTWW4R", ""+ - "SBMSVD4KKELKGZXHBUQTIROWUAPQASDX7KEJITARP4VMZ6KLUHOGPTYW", - ) - assert.NoError(t, err) - - actual, err := tx1.Base64() - assert.NoError(t, err) - assert.Equal(t, expected, actual, "base64 xdr should match") -} - -func TestAddSignatureBase64(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - txSource := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - tx1Source := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - opSource := NewSimpleAccount(kp1.Address(), 0) - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - SourceAccount: &opSource, - } - - expected, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - network.TestNetworkPassphrase, - kp0, kp1, - ) - assert.NoError(t, err) - - tx1, err := NewTransaction( - TransactionParams{ - SourceAccount: &tx1Source, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - tx1, err = tx1.AddSignatureBase64( - network.TestNetworkPassphrase, - "GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", - "TVogR6tbrWLnOc1BsP/j+Qrxpja2NWNgeRIwujECYscRdMG7AMtnb3dkCT7sqlbSM0TTzlRh7G+BcVocYBtqBw==", - ) - assert.NoError(t, err) - - tx1, err = tx1.AddSignatureBase64( - network.TestNetworkPassphrase, - "GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP", - "Iy77JteoW/FbeiuViZpgTyvrHP4BnBOeyVOjrdb5O/MpEMwcSlYXAkCBqPt4tBDil4jIcDDLhm7TsN6aUBkIBg==", - ) - - actual, err := tx1.Base64() - assert.NoError(t, err) - assert.Equal(t, expected, actual, "base64 xdr should match") -} - -func TestReadChallengeTx_validSignedByServerAndClient(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP, clientKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, clientKP.Address(), readClientAccountID) - assert.NoError(t, err) -} - -func TestReadChallengeTx_validSignedByServer(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, clientKP.Address(), readClientAccountID) - assert.NoError(t, err) -} - -func TestReadChallengeTx_invalidNotSignedByServer(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, clientKP.Address(), readClientAccountID) - assert.EqualError(t, err, "transaction not signed by "+serverKP.Address()) -} - -func TestReadChallengeTx_invalidCorrupted(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - tx64 = strings.ReplaceAll(tx64, "A", "B") - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Nil(t, readTx) - assert.Equal(t, "", readClientAccountID) - assert.EqualError( - t, - err, - "could not parse challenge: unable to unmarshal transaction envelope: "+ - "xdr:decode: switch '68174084' is not valid enum value for union", - ) -} - -func TestReadChallengeTx_invalidServerAccountIDMismatch(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(newKeypair2().Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, "", readClientAccountID) - assert.EqualError(t, err, "transaction source account is not equal to server's account") -} - -func TestReadChallengeTx_invalidSeqNoNotZero(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), 1234) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, "", readClientAccountID) - assert.EqualError(t, err, "transaction sequence number must be 0") -} - -func TestReadChallengeTx_invalidTimeboundsInfinite(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, "", readClientAccountID) - assert.EqualError(t, err, "transaction requires non-infinite timebounds") -} - -func TestReadChallengeTx_invalidTimeboundsOutsideRange(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimebounds(0, 100), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, "", readClientAccountID) - assert.Error(t, err) - assert.Regexp(t, "transaction is not within range of the specified timebounds", err.Error()) -} - -func TestReadChallengeTx_invalidTooManyOperations(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op, &op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(300), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.EqualError(t, err, "transaction requires a single manage_data operation") -} - -func TestReadChallengeTx_invalidOperationWrongType(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := BumpSequence{ - SourceAccount: &opSource, - BumpTo: 0, - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(300), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, "", readClientAccountID) - assert.EqualError(t, err, "operation type should be manage_data") -} - -func TestReadChallengeTx_invalidOperationNoSourceAccount(t *testing.T) { - serverKP := newKeypair0() - txSource := NewSimpleAccount(serverKP.Address(), -1) - op := ManageData{ - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(300), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.EqualError(t, err, "operation should have a source account") -} - -func TestReadChallengeTx_invalidDataValueWrongEncodedLength(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 45))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(300), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, clientKP.Address(), readClientAccountID) - assert.EqualError(t, err, "random nonce encoded as base64 should be 64 bytes long") -} - -func TestReadChallengeTx_invalidDataValueCorruptBase64(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAAAAAAAAAAAAAAAAAAAAAAAA"), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(300), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - require.NoError(t, err) - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, clientKP.Address(), readClientAccountID) - assert.EqualError(t, err, "failed to decode random nonce provided in manage_data operation: illegal base64 data at input byte 37") -} - -func TestReadChallengeTx_invalidDataValueWrongByteLength(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 47))), - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, serverKP) - assert.NoError(t, err) - tx64, err := tx.Base64() - assert.NoError(t, err) - - readTx, readClientAccountID, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.Equal(t, tx, readTx) - assert.Equal(t, clientKP.Address(), readClientAccountID) - assert.EqualError(t, err, "random nonce before encoding as base64 should be 48 bytes long") -} - -func TestReadChallengeTx_acceptsV0AndV1Transactions(t *testing.T) { - kp0 := newKeypair0() - tx, err := BuildChallengeTx( - kp0.Seed(), - kp0.Address(), - "SDF", - network.TestNetworkPassphrase, - time.Hour, - ) - assert.NoError(t, err) - - originalHash, err := tx.HashHex(network.TestNetworkPassphrase) - assert.NoError(t, err) - - tx.envelope.V1 = &xdr.TransactionV1Envelope{ - Tx: xdr.Transaction{ - SourceAccount: tx.envelope.SourceAccount(), - Fee: xdr.Uint32(tx.envelope.Fee()), - SeqNum: xdr.SequenceNumber(tx.envelope.SeqNum()), - TimeBounds: tx.envelope.V0.Tx.TimeBounds, - Memo: tx.envelope.Memo(), - Operations: tx.envelope.Operations(), - }, - } - tx.envelope.Type = xdr.EnvelopeTypeEnvelopeTypeTx - v1Challenge, err := marshallBase64(tx.envelope, tx.Signatures()) - assert.NoError(t, err) - - tx.envelope.V0 = &xdr.TransactionV0Envelope{ - Tx: xdr.TransactionV0{ - SourceAccountEd25519: *tx.envelope.SourceAccount().Ed25519, - Fee: xdr.Uint32(tx.envelope.Fee()), - SeqNum: xdr.SequenceNumber(tx.envelope.SeqNum()), - TimeBounds: tx.envelope.V0.Tx.TimeBounds, - Memo: tx.envelope.Memo(), - Operations: tx.envelope.Operations(), - }, - } - tx.envelope.Type = xdr.EnvelopeTypeEnvelopeTypeTxV0 - v0Challenge, err := marshallBase64(tx.envelope, tx.Signatures()) - assert.NoError(t, err) - - for _, challenge := range []string{v1Challenge, v0Challenge} { - parsedTx, clientAccountID, err := ReadChallengeTx( - challenge, - kp0.Address(), - network.TestNetworkPassphrase, - ) - assert.NoError(t, err) - - assert.Equal(t, kp0.Address(), clientAccountID) - - hash, err := parsedTx.HashHex(network.TestNetworkPassphrase) - assert.NoError(t, err) - assert.Equal(t, originalHash, hash) - } -} - -func TestReadChallengeTx_forbidsFeeBumpTransactions(t *testing.T) { - kp0 := newKeypair0() - tx, err := BuildChallengeTx( - kp0.Seed(), - kp0.Address(), - "SDF", - network.TestNetworkPassphrase, - time.Hour, - ) - assert.NoError(t, err) - - convertToV1Tx(tx) - kp1 := newKeypair1() - feeBumpTx, err := NewFeeBumpTransaction( - FeeBumpTransactionParams{ - Inner: tx, - FeeAccount: kp1.Address(), - BaseFee: 3 * MinBaseFee, - }, - ) - assert.NoError(t, err) - - feeBumpTx, err = feeBumpTx.Sign(network.TestNetworkPassphrase, kp1) - assert.NoError(t, err) - - challenge, err := feeBumpTx.Base64() - assert.NoError(t, err) - _, _, err = ReadChallengeTx( - challenge, - kp0.Address(), - network.TestNetworkPassphrase, - ) - assert.EqualError(t, err, "challenge cannot be a fee bump transaction") -} - -func TestReadChallengeTx_forbidsMuxedAccounts(t *testing.T) { - kp0 := newKeypair0() - tx, err := BuildChallengeTx( - kp0.Seed(), - kp0.Address(), - "SDF", - network.TestNetworkPassphrase, - time.Hour, - ) - - env, err := tx.TxEnvelope() - assert.NoError(t, err) - aid := xdr.MustAddress(kp0.Address()) - muxedAccount := xdr.MuxedAccount{ - Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, - Med25519: &xdr.MuxedAccountMed25519{ - Id: 0xcafebabe, - Ed25519: *aid.Ed25519, - }, - } - *env.V0.Tx.Operations[0].SourceAccount = muxedAccount - - challenge, err := marshallBase64(env, env.Signatures()) - assert.NoError(t, err) - - _, _, err = ReadChallengeTx( - challenge, - kp0.Address(), - network.TestNetworkPassphrase, - ) - errorMessage := "only valid Ed25519 accounts are allowed in challenge transactions" - assert.Contains(t, err.Error(), errorMessage) -} - -func TestVerifyChallengeTxThreshold_invalidServer(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - clientKP, - ) - assert.NoError(t, err) - - threshold := Threshold(1) - signerSummary := SignerSummary{ - clientKP.Address(): 1, - } - signersFound, err := VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.Empty(t, signersFound) - assert.EqualError(t, err, "transaction not signed by "+serverKP.Address()) -} - -func TestVerifyChallengeTxThreshold_validServerAndClientKeyMeetingThreshold(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP, - ) - assert.NoError(t, err) - - threshold := Threshold(1) - signerSummary := SignerSummary{ - clientKP.Address(): 1, - } - wantSigners := []string{ - clientKP.Address(), - } - - signersFound, err := VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.ElementsMatch(t, wantSigners, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxThreshold_validServerAndMultipleClientKeyMeetingThreshold(t *testing.T) { - serverKP := newKeypair0() - clientKP1 := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP1.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP1, clientKP2, - ) - assert.NoError(t, err) - - threshold := Threshold(3) - signerSummary := map[string]int32{ - clientKP1.Address(): 1, - clientKP2.Address(): 2, - } - wantSigners := []string{ - clientKP1.Address(), - clientKP2.Address(), - } - - signersFound, err := VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.ElementsMatch(t, wantSigners, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxThreshold_validServerAndMultipleClientKeyMeetingThresholdSomeUnused(t *testing.T) { - serverKP := newKeypair0() - clientKP1 := newKeypair1() - clientKP2 := newKeypair2() - clientKP3 := keypair.MustRandom() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP1.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - threshold := Threshold(3) - signerSummary := SignerSummary{ - clientKP1.Address(): 1, - clientKP2.Address(): 2, - clientKP3.Address(): 2, - } - wantSigners := []string{ - clientKP1.Address(), - clientKP2.Address(), - } - - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP1, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.ElementsMatch(t, wantSigners, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxThreshold_validServerAndMultipleClientKeyMeetingThresholdSomeUnusedIgnorePreauthTxHashAndXHash(t *testing.T) { - serverKP := newKeypair0() - clientKP1 := newKeypair1() - clientKP2 := newKeypair2() - clientKP3 := keypair.MustRandom() - preauthTxHash := "TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4" - xHash := "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD" - unknownSignerType := "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD" - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP1.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - threshold := Threshold(3) - signerSummary := SignerSummary{ - clientKP1.Address(): 1, - clientKP2.Address(): 2, - clientKP3.Address(): 2, - preauthTxHash: 10, - xHash: 10, - unknownSignerType: 10, - } - wantSigners := []string{ - clientKP1.Address(), - clientKP2.Address(), - } - - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP1, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.ElementsMatch(t, wantSigners, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxThreshold_invalidServerAndMultipleClientKeyNotMeetingThreshold(t *testing.T) { - serverKP := newKeypair0() - clientKP1 := newKeypair1() - clientKP2 := newKeypair2() - clientKP3 := keypair.MustRandom() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP1.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - threshold := Threshold(10) - signerSummary := SignerSummary{ - clientKP1.Address(): 1, - clientKP2.Address(): 2, - clientKP3.Address(): 2, - } - - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP1, clientKP2, - ) - assert.NoError(t, err) - - _, err = VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.EqualError(t, err, "signers with weight 3 do not meet threshold 10") -} - -func TestVerifyChallengeTxThreshold_invalidClientKeyUnrecognized(t *testing.T) { - serverKP := newKeypair0() - clientKP1 := newKeypair1() - clientKP2 := newKeypair2() - clientKP3 := keypair.MustRandom() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP1.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - threshold := Threshold(10) - signerSummary := map[string]int32{ - clientKP1.Address(): 1, - clientKP2.Address(): 2, - } - - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP1, clientKP2, clientKP3, - ) - assert.NoError(t, err) - - _, err = VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.EqualError(t, err, "transaction has unrecognized signatures") -} - -func TestVerifyChallengeTxThreshold_invalidNoSigners(t *testing.T) { - serverKP := newKeypair0() - clientKP1 := newKeypair1() - clientKP2 := newKeypair2() - clientKP3 := keypair.MustRandom() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP1.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - threshold := Threshold(10) - signerSummary := SignerSummary{} - - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP1, clientKP2, clientKP3, - ) - assert.NoError(t, err) - - _, err = VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.EqualError(t, err, "no verifiable signers provided, at least one G... address must be provided") -} - -func TestVerifyChallengeTxThreshold_weightsAddToMoreThan8Bits(t *testing.T) { - serverKP := newKeypair0() - clientKP1 := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP1.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP1, clientKP2, - ) - assert.NoError(t, err) - - threshold := Threshold(1) - signerSummary := SignerSummary{ - clientKP1.Address(): 255, - clientKP2.Address(): 1, - } - wantSigners := []string{ - clientKP1.Address(), - clientKP2.Address(), - } - - signersFound, err := VerifyChallengeTxThreshold(tx64, serverKP.Address(), network.TestNetworkPassphrase, threshold, signerSummary) - assert.ElementsMatch(t, wantSigners, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_invalidServer(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - clientKP, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP.Address()) - assert.Empty(t, signersFound) - assert.EqualError(t, err, "transaction not signed by "+serverKP.Address()) -} - -func TestVerifyChallengeTxSigners_validServerAndClientMasterKey(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP.Address()) - assert.Equal(t, []string{clientKP.Address()}, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_invalidServerAndNoClient(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP.Address()) - assert.Empty(t, signersFound) - assert.EqualError(t, err, "transaction not signed by "+clientKP.Address()) -} - -func TestVerifyChallengeTxSigners_invalidServerAndUnrecognizedClient(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - unrecognizedKP := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, unrecognizedKP, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP.Address()) - assert.Empty(t, signersFound) - assert.EqualError(t, err, "transaction not signed by "+clientKP.Address()) -} - -func TestVerifyChallengeTxSigners_validServerAndMultipleClientSigners(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP.Address(), clientKP2.Address()) - assert.Equal(t, []string{clientKP.Address(), clientKP2.Address()}, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_validServerAndMultipleClientSignersReverseOrder(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, clientKP, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP.Address(), clientKP2.Address()) - assert.Equal(t, []string{clientKP.Address(), clientKP2.Address()}, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_validServerAndClientSignersNotMasterKey(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP2.Address()) - assert.Equal(t, []string{clientKP2.Address()}, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_validServerAndClientSignersIgnoresServerSigner(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, serverKP.Address(), clientKP2.Address()) - assert.Equal(t, []string{clientKP2.Address()}, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_invalidServerNoClientSignersIgnoresServerSigner(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, serverKP.Address(), clientKP2.Address()) - assert.Empty(t, signersFound) - assert.EqualError(t, err, "transaction not signed by "+clientKP2.Address()) -} - -func TestVerifyChallengeTxSigners_validServerAndClientSignersIgnoresDuplicateSigner(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP2.Address(), clientKP2.Address()) - assert.Equal(t, []string{clientKP2.Address()}, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_validIgnorePreauthTxHashAndXHash(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - preauthTxHash := "TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4" - xHash := "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD" - unknownSignerType := "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD" - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP2.Address(), preauthTxHash, xHash, unknownSignerType) - assert.Equal(t, []string{clientKP2.Address()}, signersFound) - assert.NoError(t, err) -} - -func TestVerifyChallengeTxSigners_invalidServerAndClientSignersIgnoresDuplicateSignerInError(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP.Address(), clientKP.Address()) - assert.Empty(t, signersFound) - assert.EqualError(t, err, "transaction not signed by "+clientKP.Address()) -} - -func TestVerifyChallengeTxSigners_invalidServerAndClientSignersFailsDuplicateSignatures(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP2.Address()) - assert.Equal(t, []string{clientKP2.Address()}, signersFound) - assert.EqualError(t, err, "transaction has unrecognized signatures") -} - -func TestVerifyChallengeTxSigners_invalidServerAndClientSignersFailsSignerSeed(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - clientKP2 := newKeypair2() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP2, - ) - assert.NoError(t, err) - - signersFound, err := VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase, clientKP2.Seed()) - assert.Empty(t, signersFound) - assert.EqualError(t, err, "no verifiable signers provided, at least one G... address must be provided") -} - -func TestVerifyChallengeTxSigners_invalidNoSigners(t *testing.T) { - serverKP := newKeypair0() - clientKP := newKeypair1() - txSource := NewSimpleAccount(serverKP.Address(), -1) - opSource := NewSimpleAccount(clientKP.Address(), 0) - op := ManageData{ - SourceAccount: &opSource, - Name: "testserver auth", - Value: []byte(base64.StdEncoding.EncodeToString(make([]byte, 48))), - } - tx64, err := newSignedTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&op}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - network.TestNetworkPassphrase, - serverKP, clientKP, - ) - assert.NoError(t, err) - - _, err = VerifyChallengeTxSigners(tx64, serverKP.Address(), network.TestNetworkPassphrase) - assert.EqualError(t, err, "no verifiable signers provided, at least one G... address must be provided") -} - -func TestVerifyTxSignatureUnsignedTx(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - txSource := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - opSource := NewSimpleAccount(kp1.Address(), 0) - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - SourceAccount: &opSource, - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewTimeout(1000), - }, - ) - assert.NoError(t, err) - - err = verifyTxSignature(tx, network.TestNetworkPassphrase, kp0.Address()) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "transaction not signed by GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") - } -} - -func TestVerifyTxSignatureSingle(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - txSource := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - opSource := NewSimpleAccount(kp1.Address(), 0) - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - SourceAccount: &opSource, - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0) - assert.NoError(t, err) - err = verifyTxSignature(tx, network.TestNetworkPassphrase, kp0.Address()) - assert.NoError(t, err) -} - -func TestVerifyTxSignatureMultiple(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - txSource := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - opSource := NewSimpleAccount(kp1.Address(), 0) - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - SourceAccount: &opSource, - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - // verify tx with multiple signature - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0, kp1) - assert.NoError(t, err) - err = verifyTxSignature(tx, network.TestNetworkPassphrase, kp0.Address()) - assert.NoError(t, err) - err = verifyTxSignature(tx, network.TestNetworkPassphrase, kp1.Address()) - assert.NoError(t, err) -} -func TestVerifyTxSignatureInvalid(t *testing.T) { - kp0 := newKeypair0() - kp1 := newKeypair1() - txSource := NewSimpleAccount(kp0.Address(), int64(9605939170639897)) - opSource := NewSimpleAccount(kp1.Address(), 0) - createAccount := CreateAccount{ - Destination: "GCCOBXW2XQNUSL467IEILE6MMCNRR66SSVL4YQADUNYYNUVREF3FIV2Z", - Amount: "10", - SourceAccount: &opSource, - } - tx, err := NewTransaction( - TransactionParams{ - SourceAccount: &txSource, - IncrementSequenceNum: true, - Operations: []Operation{&createAccount}, - BaseFee: MinBaseFee, - Timebounds: NewInfiniteTimeout(), - }, - ) - assert.NoError(t, err) - - // verify invalid signer - tx, err = tx.Sign(network.TestNetworkPassphrase, kp0, kp1) - assert.NoError(t, err) - err = verifyTxSignature(tx, network.TestNetworkPassphrase, "GATBMIXTHXYKSUZSZUEJKACZ2OS2IYUWP2AIF3CA32PIDLJ67CH6Y5UY") - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "transaction not signed by GATBMIXTHXYKSUZSZUEJKACZ2OS2IYUWP2AIF3CA32PIDLJ67CH6Y5UY") - } -} From 208cfc38395f0467d799c23315ad7e58ed6f10c4 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Fri, 25 Sep 2020 14:17:29 -0700 Subject: [PATCH 5/5] Add change-log message describing (unreleased) updates --- txnbuild/CHANGELOG.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/txnbuild/CHANGELOG.md b/txnbuild/CHANGELOG.md index 33af9fe620..457bd3930d 100644 --- a/txnbuild/CHANGELOG.md +++ b/txnbuild/CHANGELOG.md @@ -3,11 +3,18 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## Unrelease +Updated to support [Protocol 14](https://github.com/stellar/go/issues/3035) operations. There are now ways to: + +* Create and claim claimable balance operations (see [CAP-23](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0023.md)) with the `[Create|Claim]ClaimableBalance` structures and their associated helpers +* Begin/end sponsoring future reserves for other accounts (see [CAP-33](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md)) with the `[Begin|End]SponsoringFutureReserves` operations +* Revoke sponsorships of various objects with the `RevokeSponsorship` operation + ## [v4.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v4.0.0) - 2020-08-31 ### Breaking changes -* Replace `BuildChallengeTx()`'s `anchorName string` parameter with `homeDomain string` +* Replace `BuildChallengeTx()`'s `anchorName string` parameter with `homeDomain string` * Add `homeDomain string` parameter to `ReadChallengeTx()`, `VerifyChallengeTxThreshold()`, and `VerifyChallengeTxSigners()` SEP-10 now requires clients to verify the `SIGNING_KEY` included in the TOML file of the service requiring authentication is used to sign the challenge and that the challenge's Manage Data operation key includes the requested service's home domain. These checks ensure the challenge cannot be used in a relay attack. @@ -104,7 +111,7 @@ The breaking changes described above support the added SEP-10 2.0 requirements f ## [v1.3.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.3.0) - 2019-07-08 * Add support for getting the hex-encoded transaction hash with `Transaction.HashHex` method. -* `TransactionEnvelope` is now available after building a transaction(`Transaction.Build`). Previously, this was only available after signing a transaction. ([#1376](https://github.com/stellar/go/pull/1376)) +* `TransactionEnvelope` is now available after building a transaction(`Transaction.Build`). Previously, this was only available after signing a transaction. ([#1376](https://github.com/stellar/go/pull/1376)) * Add support for getting the `TransactionEnvelope` struct with `Transaction.TxEnvelope` method ([#1415](https://github.com/stellar/go/issues/1415)). * `AllowTrust` operations no longer requires the asset issuer, only asset code is required ([#1330](https://github.com/stellar/go/issues/1330)). * `Transaction.SetDefaultFee` method is deprecated and will be removed in the next major release ([#1221](https://github.com/stellar/go/issues/1221)). @@ -113,7 +120,7 @@ The breaking changes described above support the added SEP-10 2.0 requirements f ## [v1.2.0](https://github.com/stellar/go/releases/tag/horizonclient-v1.2.0) - 2019-05-16 -* In addition to account responses from horizon, transactions and operations can now be built with txnbuild.SimpleAccount structs constructed locally ([#1266](https://github.com/stellar/go/issues/1266)). +* In addition to account responses from horizon, transactions and operations can now be built with txnbuild.SimpleAccount structs constructed locally ([#1266](https://github.com/stellar/go/issues/1266)). * Added `MaxTrustlineLimit` which represents the maximum value for a trustline limit ([#1265](https://github.com/stellar/go/issues/1265)). * ChangeTrust operation with no `Limit` field set now defaults to `MaxTrustlineLimit` ([#1265](https://github.com/stellar/go/issues/1265)). * Add support for building `ManageBuyOffer` operation ([#1165](https://github.com/stellar/go/issues/1165)).